How to make FeatureSummary include packages found by pkg_check_modules - cmake

My CMakeLists.txt:
cmake_minimum_required(VERSION 2.8)
project (foo)
include(FeatureSummary)
find_package(OpenSSL REQUIRED)
find_package(PkgConfig QUIET)
pkg_check_modules(JSON REQUIRED json-c)
feature_summary(WHAT ALL)
Running cmake . gives me:
-- The following REQUIRED packages have been found:
* OpenSSL
Can anyone explain the trick to make FeatureSummary also include packages found by pkg_check_modules?
UPDATE
If I create a file named FindJSON.cmake with the following code:
find_package(PkgConfig QUIET)
# --> Still using pkg_check_modules
pkg_check_modules(JSON REQUIRED QUIET json-c)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
JSON
DEFAULT_MSG
JSON_LIBRARIES
JSON_INCLUDE_DIRS)
and change CMakeLists.txt to:
cmake_minimum_required(VERSION 2.8)
project (foo)
include(FeatureSummary)
find_package(OpenSSL REQUIRED)
set(CMAKE_MODULE_PATH . ${CMAKE_MODULE_PATH})
# --> Now using find_package which still uses pkg_check_modules
find_package(JSON REQUIRED)
feature_summary(WHAT ALL)
I get:
-- The following REQUIRED packages have been found:
* OpenSSL
* JSON
That is fine. What has changed?
I use find_package_handle_standard_args. Alright let me just copy the content of FindJSON.cmake to CMakeLists.txt, instead of using it through find_package.
The new CMakeLists.txt will look like this:
cmake_minimum_required(VERSION 2.8)
project (foo)
include(FeatureSummary)
find_package(OpenSSL REQUIRED)
# --> The code from from FindJSON.cmake
find_package(PkgConfig QUIET)
# --> Still using pkg_check_modules
pkg_check_modules(JSON REQUIRED QUIET json-c)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
JSON
DEFAULT_MSG
JSON_LIBRARIES
JSON_INCLUDE_DIRS)
# <-- end of code from FindJSON.cmake
feature_summary(WHAT ALL)
and the output:
-- The following REQUIRED packages have been found:
* OpenSSL
JSON has disappeared again.
So find_package does some magic of which I am unaware.

There is magic happening inside find_package. It's storing the names of packages into global properties.
You can mess with those properties yourself:
set_property(GLOBAL APPEND PROPERTY PACKAGES_FOUND MyJunkPackage)
Check the source of FeatureSummary.cmake to see which other properties and variables it refers to in order to produce. For example, to make this package show up in the "required" list,
set_property(GLOBAL APPEND PROPERTY _CMAKE_MyJunkPackage_TYPE REQUIRED)

Use find_package_handle_standard_args and pass the according variables from find_package.
pkg_check_modules(FOO)
find_package_handle_standard_args(FOO
DEFAULT_MSG
FOO_FOUND
)
In your case replace FOO by SQLITE3.
Documentation:
https://cmake.org/cmake/help/v3.6/module/FindPackageHandleStandardArgs.html
Alternatively, to add a custom entry to the feature summary use add_feature_info.
The documentation states:
add_feature_info(<name> <enabled> <description>)
Use this macro to add
information about a feature with the given <name>. <enabled> contains
whether this feature is enabled or not, <description> is a text
describing the feature.[..]
Example for setting the info for a feature:
option(WITH_FOO "Help for foo" ON)
add_feature_info(Foo WITH_FOO "The Foo feature provides very cool stuff.")
https://cmake.org/cmake/help/v3.6/module/FeatureSummary.html

Related

How to use FetchContent_Populate with Eigen?

I want to use FetchContent to automatically manage the dependency to Eigen for my project, which works in general. However, when using the recommended method of FetchContent_Declare() and FetchContent_MakeAvailable() a subsequent call to install also installs all Eigen headers and documentation which is not necessary in my case.
To circumvent this behavior, I tried the method suggested in this answer: Disable install for FetchContent
FetchConten_Populate() however fails to fill the variables ${Eigen_SOURCE_DIR} and ${Eigen_BIN_DIR} (which the documentation told me should happen) so that I cannot call add_subdirectory().
Here is a minimal CMakeLists.txt:
cmake_minimum_required (VERSION 3.12)
project (FetchContentExample)
include (FetchContent)
FetchContent_Declare(
Eigen
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
GIT_TAG 3.4.0
)
FetchContent_GetProperties(Eigen)
if(NOT Eigen_POPULATED)
FetchContent_Populate(Eigen)
message("SRC; ${Eigen_SOURCE_DIR}") # Apparently empty?
message("BIN: ${Eigen_BINARY_DIR}") # Apparently empty?
add_subdirectory(${Eigen_SOURCE_DIR} ${Eigen_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()
add_executable(FetchContentExample
main.cpp
)
target_link_libraries (FetchContentExample
PRIVATE
Eigen3::Eigen
)
install(
TARGETS FetchContentExample
DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT Runtime
)
The same setup works fine when I use e.g.
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 5.3.0
)
instead of Eigen.
What specifically am I doing wrong when it comes to Eigen?
FetchContent_Populate() however fails to fill the variables ${Eigen_SOURCE_DIR} and ${Eigen_BINARY_DIR} (which the documentation told me should happen).
Actually, FetchContent fills variables ${eigen_SOURCE_DIR} and ${eigen_BINARY_DIR} which names are constructed from the lowercase variant of the project's name. This is written in the documentation:
FetchContent_Populate() will set three variables in the scope of the caller:
<lowercaseName>_POPULATED
This will always be set to TRUE by the call.
<lowercaseName>_SOURCE_DIR
The location where the populated content can be found upon return.
<lowercaseName>_BINARY_DIR
A directory intended for use as a corresponding build directory.
So the correct sequence of commands for EXCLUDE_FROM_ALL inclusion of Eigen would be:
FetchContent_GetProperties(Eigen)
if(NOT eigen_POPULATED)
FetchContent_Populate(Eigen)
add_subdirectory(${eigen_SOURCE_DIR} ${eigen_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()

CMake: Can I wrap an ExternalProject in some object that I can just link to my target?

I'm including this library as an external project. Based on the documentation, with some small tweaks, I have this:
# LIEF dependency
# ===========================
set(LIEF_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/LIEF")
set(LIEF_INSTALL_DIR "${LIEF_PREFIX}")
set(LIEF_INCLUDE_DIRS "${LIEF_PREFIX}/include")
# LIEF static library
set(LIEF_LIBFILE
"${LIEF_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}LIEF${CMAKE_STATIC_LIBRARY_SUFFIX}")
# URL of the LIEF repo (Can be your fork)
set(LIEF_GIT_URL "https://github.com/lief-project/LIEF.git")
# LIEF's version to be used (can be 'master')
set(LIEF_VERSION 0.11.5)
# LIEF compilation config
set(LIEF_CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DLIEF_DOC=off
-DLIEF_PYTHON_API=off
-DLIEF_EXAMPLES=off
-DLIEF_OAT=off
-DLIEF_DEX=off
-DLIEF_VDEX=off
-DLIEF_ART=off
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
)
# Specify MSVCRT on MSVC
if(MSVC)
list(APPEND ${LIEF_CMAKE_ARGS} -DLIEF_USE_CRT_RELEASE=MT)
list(APPEND ${LIEF_CMAKE_ARGS} -DLIEF_USE_CRT_DEBUG=MTd)
endif()
# External project
ExternalProject_Add(LIEF_extproj
PREFIX "${LIEF_PREFIX}"
GIT_REPOSITORY ${LIEF_GIT_URL}
GIT_TAG ${LIEF_VERSION}
INSTALL_DIR ${LIEF_INSTALL_DIR}
CMAKE_ARGS ${LIEF_CMAKE_ARGS}
BUILD_BYPRODUCTS ${LIEF_LIBFILE}
UPDATE_COMMAND ""
)
However, the original docs simply included the directories and linked separately. Can I somehow wrap these into a single target, where if I link to that target I get everything from that library?
EDIT:
My current attempt at setting up an imported target is this:
add_library(LIEF_depimpl STATIC IMPORTED)
set_target_properties(LIEF_depimpl PROPERTIES
IMPORTED_LOCATION ${LIEF_LIBFILE}
INTERFACE_INCLUDE_DIRECTORIES ${LIEF_INCLUDE_DIRS}
)
add_dependencies(LIEF_depimpl LIEF_extproj)
When I use target_link_libraries() to link LIEF against my project, CMake generates successfully, but then I get an error in the generated makefile.
add_executable(testapp lief-test.cpp)
...
# Link the executable with LIEF
target_link_libraries(testapp PUBLIC ${LIEF_depimpl})
The syntax for target_link_libraries() isn't what I thought it was. The dependency variable should not be expanded, like this:
target_link_libraries(testapp PUBLIC LIEF_depimpl)
In addition, CMake will throw an error if it can't find the include directories for an external project, so you should create that folder in your CMake file like so:
set(LIEF_INCLUDE_DIRS "${LIEF_PREFIX}/include")
file(MAKE_DIRECTORY ${LIEF_INCLUDE_DIRS})

Cmake add_library ALIAS

I am trying to figure out exactly what this line is for in the cmake file of this github json project,
add_library(${NLOHMANN_JSON_TARGET_NAME} INTERFACE)
add_library(${PROJECT_NAME}::${NLOHMANN_JSON_TARGET_NAME} ALIAS ${NLOHMANN_JSON_TARGET_NAME})
Specifically with this example, what does this allow in this cmake file that otherwise would not be possible?
I see no other references to ${PROJECT_NAME}::${NLOHMANN_JSON_TARGET_NAME} in this CMakeLists.cmake, so I am confused as to what exactly this achieves.
Edit:
The key thing that this achieves, that the comment did not make obvious to me, is that it makes the targets work with the namespaces when the project is used through add_subdirectory()
Without the alias, you can still add the library via add_subdirectory however in the target_link_libraries command you would need to omit the namespace:
project(mySuperApp)
set(mySuperApp_SRC src/main.c)
add_subdirectory(thirdparty/json)
add_executable(${PROJECT_NAME} ${mySuperApp_SRC})
target_link_libraries(${PROJECT_NAME} PRIVATE nlohmann_json)
If you did that but then decided to use find_package to include the library (as opposed to add_subdirectory), you would need to change target_link_libraries to use the namespaced targets i.e.
project(mySuperApp)
set(mySuperApp_SRC src/main.c)
find_package(nlohmann_json REQUIRED)
add_executable(${PROJECT_NAME} ${mySuperApp_SRC})
target_link_libraries(${PROJECT_NAME} PRIVATE nlohmann_json::nlohmann_json)
by adding the alias, the target_link_libraries using the namespaced version (i.e. nlohmann_json::nlohmann_json) will work in either case and not require a change if you later decide to switch from find_package to add_subdirectory).
It allows you to add the library with find_package OR add_subdirectory using the same target name for both:
# creates nlohmann_json::nlohmann_json
find_package(nlohmann_json REQUIRED)
if (nlohmann_json_NOT_FOUND)
# creates nlohmann_json AND nlohmann_json::nlohmann_json
add_subdirectory(thirdparty/json)
endif()
add_executable(your_target_name ${your_target_sources})
target_link_libraries(your_target_name PRIVATE nlohmann_json::nlohmann_json)
Without the alias, you would need:
# creates nlohmann_json::nlohmann_json
find_package(nlohmann_json REQUIRED)
if (NOT nlohmann_json_FOUND)
# creates only nlohmann_json
add_subdirectory(thirdparty/json)
endif()
add_executable(your_target_name ${your_target_sources})
if (nlohmann_json_FOUND)
target_link_libraries(your_target_name PRIVATE nlohmann_json::nlohmann_json)
else()
target_link_libraries(your_target_name PRIVATE nlohmann_json)
endif()
This will allow using nlohmann/json project by adding it into your super project with add_subdirectory(...)
For example simple project structure:
<root project>\
\thirdparty\json <<-- git submodule to https://github.com/nlohmann/json
\include\
\src\
CMakeLists.txt
In your project CMakeLists.txt
...
project(mySuperApp)
set(mySuperApp_SRC src/main.c)
# can under some conditions...
add_subdirectory(thirdparty/json)
add_executable(${PROJECT_NAME} ${mySuperApp_SRC})
target_link_libraries(${PROJECT_NAME} PRIVATE nlohmann_json::nlohmann_json)
Using git's blame function shows that line was added in this commit: 33a2154, which has the following comment attached:
CMake convention is to use a project namespace, i.e. Foo::, for imported
targets. When multiple targets are imported from a project, this looks
like Foo::Bar1 Foo::Bar2, etc. This adds the nlohmann_json:: namespace to
the exported target names.
This also allows the generated project config files to be used from the
build directory instead of just the install directory.

CMake get_property at "generate time" instead of "configure time" for INTERFACE_INCLUDE_DIRECTORIES

I have multiple libraries which utilize the target_include_directories(myLib PUBLIC myLib/inc) command to populate their INTERFACE_INCLUDE_DIRECTORIES property. Now if I build a target which is linked against such a library the INTERFACE_INCLUDE_DIRECTORIES are propagated (as wanted) to the target.
My problem occurs when I try to use the get_property command to snatch the list of INCLUDE_DIRECTORIES of such a created target as - according to this already answered question - the linking of libraries is only evaluated at generate time and thus also the propagation of the INCLUDE_DIRECTORIES is only done at generate time.
What happens is that the list created via get_property is empty / incomplete. Is there a way to force CMake to evaluate the list again at generate time when all linking is also done? I want to use some string-operations on the list and use the result for a custom_command later on...
I'm using:
Cmake version 3.5.0
Scientific Linux release 7.2
Minimal Example of the problem
Main CMakeLists.txt
cmake_minimum_required (VERSION 2.8)
project(MINIMAL LANGUAGES CXX)
add_subdirectory(${PROJECT_SOURCE_DIR}/libA)
add_subdirectory(${PROJECT_SOURCE_DIR}/libB)
libA CMakeLists.txt
cmake_minimum_required (VERSION 2.8)
project (libA)
add_library(libA ${Some_Sources} ${Some_Header})
# Set include_directories, this populates INTERFACE_INCLUDE_DIRECTORIES
target_include_directories(libA PUBLIC "../libA/src")
libB CMakeLists.txt
cmake_minimum_required (VERSION 2.8)
project (libB)
add_library(libB ${Some_Sources} ${Some_Header})
target_link_libraries(libB PUBLIC libA)
target_include_directories(libB PUBLIC "../libB/src")
# this contains only the value set here directly as
# get_property is evaluated at configure_time
get_property(INC_DIR_LIST TARGET libB PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
message(${INC_DIR_LIST})
# >>> Output: ../libB/src
# this expression is evaluated at generate time,
# thus it contains all needed information as the linking is done already
file(GENERATE
OUTPUT "includes.txt"
CONTENT "$<TARGET_PROPERTY:libB,INTERFACE_INCLUDE_DIRECTORIES>\n
# >>> Output in text-file: ../libB/src;../libA/src
Possible (but bad??) solution
As I know that (in the example) libB is linked against libA I could manually add:
get_property(INC_DIRS_LIBA TARGET libA PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
target_include_directories(libB PUBLIC INC_DIRS_LIBA)
This would work as the property is now set correctly at configure time BUT it is now set twice at generate time (as CMake sets it again automatically when evaluating the linking) and it seems just wrong to me...

Simple CMakeLists.txt that reflects typical directory structure (/src, /inc, /bin subdirectories)

I am struggling to make a CMakeList.txt file to reflect a simple, typical makefile. The original is here http://pastebin.com/S9Czr1pt .
I tried many things (like SET(SOURCE ... and SET(HEADERS... ) to add /src, /lib and /third-party//include ), but I have no luck.
Can anyone either help me out or point to a tutorial that does this thing?
This is just an out of the blue skeleton - please see the CMake documentation for details of each function:
cmake_minimum_required(VERSION 2.6)
# Project name, can be used as target name too
project(Example)
# Search all .cpp files within src - recursive!
# You can add all source files by hand here too
file(GLOB_RECURSE SRCS "src/*.cpp")
# Add include path (you can also add it's sub directories
include_directories("include")
# Search for packages -- PLEASE NOTE: many libraries provide alternative's to this
# which often provide more functionality
find_package(PkgConfig REQUIRED)
# TODO: add proper pkg modul search info's and change the variable's name
pkg_search_module(PACKAGE_NO1 ...)
# Show some messages (optional)
if( (PACKAGE_NO1 )
include_directories(${(PACKAGE_NO1_INCLUDE_DIRS})
message(STATUS "Using OpenSSL ${(PACKAGE_NO1_VERSION}")
else()
# do error handling
endif()
# Add compiler flags
add_definitions(-std=c++11 -Wall) # -g O3 etc are added according to Release / Debug compilation
# Build a executable target (1st param: Target name; 2nd: source files)
add_executable(${PROJECT_NAME} ${SRCS})