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

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.

Related

Cmake FetchContent have source and binary in different directories?

I want to reuse the source folder of my external libraries declared by FetchContent to avoid multiple downloads for multiple build configurations (debug/release/android/emscripten). To clarify, the build configurations have different binary directories e.g. /build/debug, build/release...
The ideal solution would be to have the source of the external depenendency in {CMAKE_SOURCE_DIR}/ext and the binary dir in {CMAKE_BINARY_DIR}/${name}-build
When using FETCHCONTENT_BASE_DIR (see example below) the source directory gets shared but also the binary directory, which triggers a complete rebuild whenever I switch between build configurations.
There is also the BINARY_DIR parameter for FetchContent_Populate, but it seems not do give the desired results.
set(FETCHCONTENT_BASE_DIR ${CMAKE_SOURCE_DIR}/depenendencies)
FetchContent_Declare(
${name}
GIT_REPOSITORY ${url}
GIT_TAG ${tag}
GIT_PROGRESS ON
)
FetchContent_GetProperties(${name})
if (NOT ${name}_POPULATED)
FetchContent_Populate(
${name}
)
add_subdirectory(${${name}_SOURCE_DIR} ${${name}_BINARY_DIR})

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.

Semicolon separated list to cmake command in cmake file

I am building amazon's aws-sdk-cpp in my application. So i am extracting the content from aws-sdk-cpp.tar.gz file and running as ExternalProject.
ExternalProject_Add(awssdk
PREFIX DIR
URL ${CMAKE_CURRENT_SOURCE_DIR}/${AWS_SDK_SRC_TAR}
BUILD_COMMAND ${CMAKE_COMMAND} cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_ONLY='dynamodb;kenisis' -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/Ext/Aws/CtcInstall/${CMAKE_INSTALL_PREFIX} -DENABLE_TESTING=OFF ${CMAKE_BINARY_DIR}/source
But, when i pass -DBUILD_ONLY='dynamodb;kenisis'(supported by aws cmake file), Build is failing. If i pass -DBUILD_ONLY=dynamodb only dynamodb related targets are getting generated. But i need kenisis related targets also. How can i pass that semicolon separated list in ExternalProject_Add.
Doing some testing myself - including the hint in #usr1234567's comment - I did run the following successfully (for easier reproduciblility I've put the Git address in the example):
ExternalProject_Add(
awssdk
PREFIX DIR
GIT_REPOSITORY https://github.com/aws/aws-sdk-cpp.git
CMAKE_CACHE_ARGS
-DCMAKE_BUILD_TYPE:String=${CMAKE_BUILD_TYPE}
-DBUILD_ONLY:String=dynamodb\\;kinesis
-DCMAKE_INSTALL_PREFIX:String=${CMAKE_BINARY_DIR}/Ext/Aws/CtcInstall
-DENABLE_TESTING:String=OFF
)
The awkward double backslashes are necessary because the ExternalProject_Add() itself is a CMake script function that goes through several loops of parameter extension.
Alternative
A more generic way would be to use a $<SEMICOLON> generator expression like this:
ExternalProject_Add(
awssdk
PREFIX DIR
GIT_REPOSITORY https://github.com/aws/aws-sdk-cpp.git
CMAKE_ARGS
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DBUILD_ONLY=dynamodb$<SEMICOLON>kinesis
-DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/Ext/Aws/CtcInstall
-DENABLE_TESTING=OFF
)
Just for demonstration purposes I'm also using an alternative way of passing the arguments here.

cmake: external project update and a work offline

I was using external projects to keep libraries up to date for a long time
something like:
include(AddAsio)
and script AddAsio.cmake like this:
cmake_minimum_required(VERSION 2.8.8)
include(ExternalProject)
find_package(Git REQUIRED)
ExternalProject_Add(
asio
PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/include/ext
GIT_REPOSITORY https://github.com/chriskohlhoff/asio.git
GIT_TAG master
TIMEOUT 10
UPDATE_COMMAND ${GIT_EXECUTABLE} pull
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
LOG_DOWNLOAD ON
)
ExternalProject_Get_Property(asio source_dir)
set(ASIO_INCLUDE_DIR ${source_dir}/asio/include CACHE INTERNAL "Path to include folder for Asio")
include_directories(${INCLUDE_DIRECTORIES} ${ASIO_INCLUDE_DIR})
message("asio source dir" ${ASIO_INCLUDE_DIR})
there was no issue for a long time, however yesterday I've faced with a problem when my internet provider was not working and I tried to work offline.
this script tries to update (git pull, look at external project definition) each time of build, even all required headers do exist there. and breaks build process in case of no connection.
any idea how to keep this mechanism of components update but do not stop a building in case of libraries already exist locally?
AFAIK, there is no easy way to make an external project not to update by examining if certain files exist. However, you could set UPDATE_DISCONNECTED to an external project or set EP_UPDATE_DISCONNECTED property of a directory to disable update of external projects. Along with a build option, the process could be made dynamically. For example, you could build the following project without updating foo and bar by configuring with the command cmake -DBUILD_OFFLINE=ON
cmake_minimum_required (VERSION 2.8.11)
project (myrootproject C)
add_library(hello SHARED hello.c)
option(BUILD_OFFLINE "Build offline" OFF)
if (${BUILD_OFFLINE})
set_property(DIRECTORY ${myrootproject_SOURCE_DIR}
PROPERTY EP_UPDATE_DISCONNECTED 1)
endif()
include(ExternalProject)
ExternalProject_Add(foo
SVN_REPOSITORY svn://foobar.org/
)
ExternalProject_Add(bar
GIT_REPOSITORY https://github.com/blah/blah
)

CMAKE with ROS to create external projects locally?

I am working with ROS and wanted to modify the ardrone driver of autonomy lab. Unfortunately I have problems to build external libraries with CMake.
The problem is following:
The compiling original CMakeLists includes an external project:
include(ExternalProject)
ExternalProject_Add(ardronelib
GIT_REPOSITORY git://github.com/AutonomyLab/ardronelib.git
GIT_TAG 2f987029c55531e4c0119c3600f9c57f935851ed
PREFIX ${CATKIN_DEVEL_PREFIX}
CONFIGURE_COMMAND echo "No configure"
BUILD_COMMAND make
INSTALL_COMMAND make install INSTALL_PREFIX=${CATKIN_DEVEL_PREFIX}/lib/
BUILD_IN_SOURCE 1
)
To modify this package, I downloaded it into my sourcefolder:
catkin_ws/src/my_project/
ardronelib/
src/ ...
launch/ ...
...
the ardronelib folder contains the makefile of the downloaded git project.
ardronelib/
ARDroneLib
.git
.travis.yml
LICENSE
Makefile
README.md
Basically I want to have the same libraries installed and linked as in the original version, but from this local folder.
My approach with:
include(ExternalProject)
ExternalProject_Add(ardronelib
SOURCE_DIR=${PROJECT_SOURCE_DIR}/ardronelib
PREFIX ${CATKIN_DEVEL_PREFIX}
CONFIGURE_COMMAND echo "No configure"
BUILD_COMMAND make
INSTALL_COMMAND make install INSTALL_PREFIX=${CATKIN_DEVEL_PREFIX}/lib/
BUILD_IN_SOURCE 1
)
and several other attempts didn't work. Any ideas how to build the project so that later I can use the locally build "ardronelib" within my project?
As the library is quite complex, the direct linking of the source files
would be to costly. Any ideas how I can use the same build structure, but instead of
GIT_REPOSITORY git://github.com/AutonomyLab/ardronelib.git
GIT_TAG 2f987029c55531e4c0119c3600f9c57f935851ed
us the local version:
${PROJECT_SOURCE_DIR}/ardronelib
I would be reaaally happy for any clues.
Thanks a lot in advance!