Missing header file when CMake-ing glog - c++

I wanted to use logging in my app. In the CMake file I made (the relevant part see below) for a while everything runs smoothly, gflags seems to be installed OK. However, when compiling glog, I receive the error message:
fatal error:
'gflags/gflags.h' file not found
The missing file is found in the generated code, so I guess that some switches/options are set in some incorrect way. Or, the other thing that I download the files from a wrong site. (I found and downloaded several pathched glog files, they all present me error messages, with different ones.) How can I fix it? (I would prefer a non-manual patch)
# Download and install GoogleFlags
ExternalProject_Add(
gflags
URL https://github.com/gflags/gflags/archive/master.zip
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/gflags
INSTALL_COMMAND ""
)
# Create a libgflags target to be used as a dependency by test programs
add_library(libgflags IMPORTED STATIC GLOBAL)
add_dependencies(libgflags gflags)
# Set gflag properties
ExternalProject_Get_Property(gflags source_dir binary_dir)
set_target_properties(libgflags PROPERTIES
"IMPORTED_LOCATION" "${binary_dir}/libgflags.a"
"IMPORTED_LINK_INTERFACE_LIBRARIES" "${CMAKE_THREAD_LIBS_INIT}"
)
include_directories("${source_dir}/include")
# Download and install GoogleLog
ExternalProject_Add(
glog
URL https://github.com/emzeat/google-glog/archive/master.zip
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/glog
INSTALL_COMMAND ""
)
# Create a libglog target to be used as a dependency by test programs
add_library(libglog IMPORTED STATIC GLOBAL)
add_dependencies(libglog glog)
# Set glog properties
ExternalProject_Get_Property(glog source_dir binary_dir)
set_target_properties(libglog PROPERTIES
"IMPORTED_LOCATION" "${binary_dir}/libglog.a"
"IMPORTED_LINK_INTERFACE_LIBRARIES" "${CMAKE_THREAD_LIBS_INIT}"
# "INTERFACE_INCLUDE_DIRECTORIES" "${source_dir}/include"
)
# I couldn't make it work with INTERFACE_INCLUDE_DIRECTORIES
include_directories("${source_dir}/include")

Related

CMake - Alternate option to FetchContent when the source file already exists locally

I tried building the ORTools github package locally using cmake and it builds without any errors. However the environment which we are planning to ultimately use does not have an outbound network connection. The problem I see is that https://github.com/google/or-tools/blob/v9.4/cmake/dependencies/CMakeLists.txt performs a Git Clone to download the dependencies and add them. Since there is no outbound network access this step fails and I'm unable to build the dependency. To circumvent this we are planning to manually download the dependencies and add them to https://github.com/google/or-tools/blob/v9.4/cmake/dependencies/ folder.
I'm pretty new to CMake and I'm not sure what changes have to be made to accommodate this.
For example, what would the following snippet need to be changed to if I cloned Zlib v1.2.11 repository and added it to https://github.com/google/or-tools/blob/v9.4/cmake/dependencies/?
# ##############################################################################
# ZLIB
# ##############################################################################
if(BUILD_ZLIB)
message(CHECK_START "Fetching ZLIB")
list(APPEND CMAKE_MESSAGE_INDENT " ")
FetchContent_Declare(
zlib
GIT_REPOSITORY "https://github.com/madler/ZLIB.git"
GIT_TAG "v1.2.11"
PATCH_COMMAND git apply --ignore-whitespace "${CMAKE_CURRENT_LIST_DIR}/../../patches/ZLIB.patch")
FetchContent_MakeAvailable(zlib)
list(POP_BACK CMAKE_MESSAGE_INDENT)
message(CHECK_PASS "fetched")
endif()
Can FetchContent_Declare be used to point to a directory that already contains the source files? What's the alternative?
You could specify SOURCE_DIR parameter for FetchContent_Declare and omit download options:
FetchContent_Declare(
zlib
SOURCE_DIR <path/to/existing/directory>
PATCH_COMMAND git apply --ignore-whitespace "${CMAKE_CURRENT_LIST_DIR}/../../patches/ZLIB.patch"
)
This works in the same way as in ExternalProject_Add command, which options are accepted by FetchContent_Declare.

CMake declare dependency of function on ExternalProject_Add

TLDR:
My problem is that CMake starts executing this function before
downloading the repository. I would like to declare a dependency for
that function on ExternalProject_Add so that CMake understands that it
should download, build and then run the function.
Context:
I have a cmake module SomeModule.cmake which is supposed to add flatbuffers as an external project from its repository and build it. The build would produce flatbuffers compiler executable which I intend to use in some/directory/CMakeLists.txt file to generate c++ header files from a flatbuffers schema. So in that same CMake module that I use ExternalProject_Add, I have declared a CMake function that generates header files from a given set of schema files and somewhere in some/directory/CMakeLists.txt I call that function.
My problem is that CMake starts executing this function before downloading the repository. I would like to declare a dependency for that function on ExternalProject_Add so that CMake understands that it should download, build and then run the function.
Enough talk. Here's relevant parts of the code:
SomeModule.cmake:
include(ExternalProject)
set(flatbuffers_CMAKE_ARGS
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
"-DFLATBUFFERS_BUILD_TESTS=OFF"
"-DFLATBUFFERS_BUILD_FLATC=ON"
"-DFLATBUFFERS_BUILD_FLATHASH=OFF"
"-DCMAKE_INSTALL_PREFIX=${OTS_DEPENDENCIES}"
)
ExternalProject_Add(
flatbuffers
GIT_REPOSITORY "https://github.com/google/flatbuffers.git"
GIT_TAG "v1.9.0"
SOURCE_DIR "${OTS_DEPDENDENCIES_DIR}/flatbuffers"
BINARY_DIR "${OTS_DEPDENDENCIES_DIR}/flatbuffers"
CMAKE_ARGS "${flatbuffers_CMAKE_ARGS}"
INSTALL_COMMAND ""
)
ExternalProject_Get_Property(flatbuffers SOURCE_DIR)
ExternalProject_Get_Property(flatbuffers BINARY_DIR)
set(flatbuffers_SOURCE_DIR ${SOURCE_DIR})
set(flatbuffers_BINARY_DIR ${BINARY_DIR})
set(flatbuffers_INCLUDE_DIR ${flatbuffers_SOURCE_DIR}/include)
set(flatbuffers_FLATC_EXECUTABLE ${flatbuffers_BINARY_DIR}/flatc)
# please assume that the variables above are all set to appropriate values
function(FlatbuffersGenerateCpp SCHEMA_FILES GENERATED_DIR GENERATED_CXX)
foreach(SCHEMA_FILE ${SCHEMA_FILES})
get_filename_component(NAME ${SCHEMA_FILE} NAME_WE)
set(GENERATED_HEADER_FILE_PATH ${GENERATED_DIR}/${NAME}_generated.h)
message(STATUS "attempting to generate: ${GENERATED_HEADER_FILE_PATH}")
add_custom_command(
DEPENDS ${flatbuffers_FLATC_EXECUTABLE}
OUTPUT ${GENERATED_HEADER_FILE_PATH}
COMMAND ${flatbuffers_FLATC_EXECUTABLE} -o ${GENERATED_DIR} -c ${SCHEMA_FILE}
COMMENT "generating flatbuffers c++ header file: ${GENERATED_HEADER_FILE_PATH}"
)
list(APPEND GENERATED_FILES ${GENERATED_HEADER_FILE_PATH})
endforeach()
message(STATUS "generated c++ header files: ${GENERATED_FILES}")
set(${GENERATED_CXX} ${GENERATED_FILES} PARENT_SCOPE)
endfunction()
And some/directory/CMakeLists.txt:
# cmake module path is properly set so the following works:
include(SomeModule)
set(flatbuffers_GENERATED_INCLUDES_DIR
${CMAKE_BINARY_DIR}/generated/config/flatbuffers
)
FlatbuffersGenerateCpp(
"${flatbuffers_SCHEMAS}"
"${flatbuffers_GENERATED_INCLUDES_DIR}"
flatbuffers_GENERATED_CXX
)
add_library(
my_framework
SHARED
${THE_PUBLIC_HEADER_FILES}
${THE_IMPL_SOURCE_FILES}
${THE_IMPL_HEADER_FILES}
${flatbuffers_GENERATED_CXX}
)
add_dependencies(my_framework flatbuffers ${flatbuffers_GENERATED_CXX})
target_include_directories(my_framework PRIVATE ${flatbuffers_INCLUDE_DIR})
target_include_directories(my_framework PRIVATE ${CMAKE_SOURCE_DIR})
target_include_directories(my_framework PRIVATE ${CMAKE_BINARY_DIR}/generated)
set_source_files_properties(${flatbuffers_GENERATED_CXX} PROPERTIES GENERATED TRUE)
I did start modifying my code based on comment posted by Tsyvarev:
CMake functions are executed at configure stage, so you need to build the external project at configure stage too.
While I trusted that his proposed solution would work, I was slightly uncomfortable and kept thinking that there has to be a more elegant solution. I consulted with a colleague and came up with a better solution which is as simple as the following diff (which removes ${flatbuffers_GENERATED_CXX}).
- add_dependencies(my_framework flatbuffers ${flatbuffers_GENERATED_CXX})
+ add_dependencies(my_framework flatbuffers)
we reviewed that the problem with the code in question is that as is, CMake reads add_dependencies(my_framework flatbuffers ${flatbuffers_GENERATED_CXX}) and understands that it needs ${flatbuffers_GENERATED_CXX} as target to build my_framework so it proceeds with running the function. But there is no way for it to understand that the function depends on the external project. Now if we remove the explicit dependency declaration of ${flatbuffers_GENERATED_CXX}, CMake defers running the function to after resolving other dependencies (flatbuffers target) which will effectively download and build the external project prior to running the project.

How to get the output path of a target defined by ExternalProject?

I'm building Google's FlatBuffers as a dependency for my own project and I need to compile a schema at build-time. I don't want to use BuildFlatBuffers.cmake or FindFlatBuffers.cmake because I'm using a specific version and I can't rely on it being locally installed.
This is a simplified version of my CMakeLists.txt:
ExternalProject_Add (
flatbuf
URL "https://github.com/google/flatbuffers/archive/v1.8.0.tar.gz"
CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
)
add_custom_target (
flatbuf_schema
PREFIX ${FLATBUF_PREFIX}
DEPENDS flatbuf
COMMAND ${FLATBUF_PREFIX}/src/flatbuf-build/flatc --cpp ${FLATBUF_SCHEMA}
)
It works fine for Make and Ninja but fails in Xcode, which builds flatc in the Debug directory.
I thought about these possible solutions:
use add_subdirectory instead of ExternalProject_Add so that I can use ${FLATBUFFERS_FLATC_EXECUTABLE} or $<TARGET_FILE:flatc>;
manually assign a RUNTIME_OUTPUT_DIRECTORY for flatbuf;
search for flatc in multiple paths (not portable; I also don't know how to make it happen at build-time).
I tried (2) and (3) but without success. As for (1), I'm not sure it's a good idea. How can I build schemas in a portable manner?
You can use ExternalProject_Get_Property, something like this...
note: I suppose you don't even need to install flatbuf, just build it and use it.
ExternalProject_Add (
flatbuf_project
URL "https://github.com/google/flatbuffers/archive/v1.8.0.tar.gz"
CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
INSTALL_COMMAND ""
)
ExternalProject_Get_Property(flatbuf_project source_dir)
ExternalProject_Get_Property(flatbuf_project binary_dir)
# Export flatbuf executable to consume schema file during build
add_executable(flatbuf::flatbuf IMPORTED)
set_target_properties(flatbuf::flatbuf PROPERTIES IMPORTED_LOCATION
"${binary_dir}/flatc")
add_dependencies(flatbuf::flatbuf flatbuf_project)
add_custom_target(flatbuf_schema
PREFIX ${FLATBUF_PREFIX}
COMMAND flatbuf::flatbuff --cpp ${FLATBUF_SCHEMA}
)
note2:
If COMMAND specifies an executable target name (created by the add_executable() command) it will automatically be replaced by the location of the executable created at build time. If set, the CROSSCOMPILING_EMULATOR executable target property will also be prepended to the command to allow the executable to run on the host. Additionally a target-level dependency will be added so that the executable target will be built before this custom target.
note3:
target ALIAS are not working on IMPORTED target unfortunately...
Apparently CMake provides a variable to solve this exact problem, i.e. CMAKE_CFG_INTDIR (docs). The path to the flatc executable should then be
ExternalProject_Get_Property(flatbuf BINARY_DIR)
set (FLATC "${BINARY_DIR}/${CMAKE_CFG_INTDIR}/flatc")

Specify RPATH for externally built CMAKE project

How do I specify an RPATH for a project that was built externally in CMake (using 3.0.0) via ExternalProject_Add() macro?
For reference, let's say my ExternalProject is SFML. My external project call looks like:
set(SFML_INSTALL_PATH "${CMAKE_CURRENT_BINARY_DIR}/ext-deps/ext-sfml-build")
set(SFML_RPATH "${SFML_INSTALL_PATH}/src/lib")
set(SFML_CMAKE_ARGS -DCMAKE_BUILD_TYPE:STRING=Release -DUSE_STATIC_LIBS:BOOL=true
-DCMAKE_INSTALL_PREFIX:PATH=${SFML_INSTALL_PATH}
-DCMAKE_INSTALL_RPATH:PATH=${SFML_RPATH})
ExternalProject_Add(ext-sfml
GIT_REPOSITORY "${SFML_REPO}"
GIT_TAG "${SFML_TAG}"
URL SFML_URL
URL_HASH 256=${SFML_SHA256}
CMAKE_ARGS "${SFML_CMAKE_ARGS}"
TMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/${EXT_DEPS_PREFIX}/ext-sfml-tmp"
STAMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/${EXT_DEPS_PREFIX}/ext-sfml-stamp"
INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/${EXT_DEPS_PREFIX}/ext-sfml-build"
SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/${EXT_DEPS_PREFIX}/ext-sfml"
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/${EXT_DEPS_PREFIX}/ext-sfml-build"
TEST_COMMAND "")
The error command I get when I run the build step (configuration is fine) is
Make Error at src/cmake_install.cmake:45 (file):
file RPATH_CHANGE could not write new RPATH:
/home/hendrix/repo/project/build/ext_deps/ext_sfml-build/lib
to the file:
/home/hendrix/repo/project/build/ext_deps/ext_sfml-build/bin/exe
Finally, this "/home/hendrix/repo/project/build/ext_deps/ext_sfml-build/lib" is the line I'm trying to modify. It appears that it is the value of my ${CMAKE_INSTALL_RPATH} variable but containing the install prefix of my ext-dep project and not the top-level master project.
I figured it out (thanks to Tsyvarev's comments). The externally built project that I was using set it's own RPATH. So the string that I found in the error message was that RPATH, not any RPATH which I set in my "top-level" project.
It appears that setting the RPATH of an external project can not override that external project's internally set RPATH (if it is using CMAKE).

How to get a header only library portably from version control using CMake?

For a typical C++ header only library located on, e.g., github, at: https://github.com/username/library_name,
has a directory structure with an include/library_name folder like:
include/library_name
containing all the library sources. This is typically installed by users to, e.g., under Linux: /usr/local/include/library_name.
I need a cmake script for using the library in external projects portably (across Linux, MacOs, BSD, Windows).
It should:
find if the library is installed, if the version is over a threshold, use the installed library
otherwise, get the library from github, configure it as an external project, and put it in the system include path so that it can be used by the project as if it were installed.
What is the correct way of achieving this with CMake?
I finally got it to work, this FindLibraryName.cmake tries to find the library and if it doesn't find it, gets it from github and sets it up as an external project:
# Find the Library_Name include directory
# The following variables are set if Library_Name is found.
# Library_Name_FOUND - True when the Library_Name include directory is found.
# Library_Name_INCLUDE_DIRS - The path to where the poco include files are.
# If Library_Name is not found, Library_Name_FOUND is set to false.
find_package(PkgConfig)
# Allow the user can specify the include directory manually:
if(NOT EXISTS "${LIBRARY_NAME_INCLUDE_DIR}")
find_path(LIBRARY_NAME_INCLUDE_DIR
NAMES library_name/library_name.hpp
DOC "Library_Name library header files"
)
endif()
if(EXISTS "${LIBRARY_NAME_INCLUDE_DIR}")
include(FindPackageHandleStandardArgs)
mark_as_advanced(LIBRARY_NAME_INCLUDE_DIR)
else()
include(ExternalProject)
ExternalProject_Add(library_name
GIT_REPOSITORY https://github.com/username/library_name.git
TIMEOUT 5
CMAKE_ARGS -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
PREFIX "${CMAKE_CURRENT_BINARY_DIR}"
INSTALL_COMMAND "" # Disable install step, is a header only lib!
)
# Specify include dir
ExternalProject_Get_Property(library_name source_dir)
set(LIBRARY_NAME_INCLUDE_DIRS ${source_dir}/include)
endif()
if(EXISTS "${LIBRARY_NAME_INCLUDE_DIR}")
set(Library_Name_FOUND 1)
else()
set(Library_Name_FOUND 0)
endif()
Then in the projects CMakeLists.txt, just add:
find_package(Library_Name)
include_directories(${LIBRARY_NAME_INCLUDE_DIRS})
You can replace GIT with SVN above and provide the URL of an svn repo and it will work as well. Other version control systems are also available.