Is there a way to make an external dependency depend of two others external dependencies of a project ?
Here is my situation
core depdends on boost_program_options and on quetzal
quetzal depends on gdal and boost_filesystem and boost_unit_test_framework
How to orchestrate these dependencies ? I have been trying the following approach:
The root CMakelist.txt contains:
list(APPEND BOOST_COMPONENTS_REQUIRED program_options)
add_subdirectory(external/upstream)
ExternalProject_Add(
${PROJECT_NAME}_core
DEPENDS
boost_external
gdal_external
quetzal_external
SOURCE_DIR
${CMAKE_CURRENT_LIST_DIR}/FTD_core
CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX=${STAGED_INSTALL_PREFIX}
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}
-DCMAKE_CXX_EXTENSIONS=${CMAKE_CXX_EXTENSIONS}
-DCMAKE_CXX_STANDARD_REQUIRED=${CMAKE_CXX_STANDARD_REQUIRED}
CMAKE_CACHE_ARGS
-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS}
-DCMAKE_LIBRARY_PATH:PATH=${STAGED_INSTALL_PREFIX}
# Forward externals to the core project
-DCMAKE_INCLUDE_PATH:PATH=${BOOST_INCLUDEDIR}
-DCMAKE_LIBRARY_PATH:PATH=${BOOST_LIBRARYDIR}
-DCMAKE_INCLUDE_PATH:PATH=${GDAL_INCLUDE_DIR}
-DCMAKE_LIBRARY_PATH:PATH=${GDAL_LIBRARY}
-DCMAKE_INCLUDE_PATH:PATH=${QUETZAL_INCLUDE_DIR}
BUILD_ALWAYS
1
INSTALL_COMMAND
""
)
The external/upstream/quetzal/CMakeLists.txt contains:
list(APPEND BOOST_COMPONENTS_REQUIRED unit_test_framework filesystem)
include(ExternalProject)
ExternalProject_Add(quetzal_external
DEPENDS
boost_external
gdal_external
GIT_REPOSITORY https://github.com/Becheler/quetzal.git
GIT_TAG master
CMAKE_ARGS
-DCMAKE_LIBRARY_PATH:PATH=${STAGED_INSTALL_PREFIX}
-DCMAKE_INCLUDE_PATH:PATH=${BOOST_INCLUDEDIR}
-DCMAKE_LIBRARY_PATH:PATH=${BOOST_LIBRARYDIR}
-DCMAKE_INCLUDE_PATH:PATH=${GDAL_INCLUDE_DIR}
-DCMAKE_LIBRARY_PATH:PATH=${GDAL_LIBRARY}
-DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}
-DCMAKE_PREFIX_PATH=${CMAKE_CURRENT_BINARY_DIR}
-DBUILD_TESTS=Off
UPDATE_COMMAND ""
)
external/upstream/boost/CMakeLists.txt and external/upstream/gdal/CMakeLists.txt seem to behave adequately. But for some reason external/upstream/quetzal/CMakeLists.txt finds the system boost installation (1.71), when I want it to find the locally built boost installation (1.65).
I think something is wrong with my way to forward the boost dependency to the quetzal dependency, but I don't even know if the general approach is correct or not.
Related
Im trying to use two external git projects in my project, where one depends on the other. However I cannot make it work.
FetchContent_Declare(
spdlog
GIT_REPOSITORY https://github.com/gabime/spdlog.git
GIT_TAG v1.11.0
)
FetchContent_MakeAvailable(spdlog)
FetchContent_Declare(
spdlog_setup
GIT_REPOSITORY https://github.com/guangie88/spdlog_setup.git
GIT_TAG v0.3.2
)
FetchContent_MakeAvailable(spdlog_setup)
The second FetchContent fails because it cannot find the spdlog lib.
If I look at the CMakeLists of that depenency I see:
if (EXISTS ${CMAKE_SOURCE_DIR}/deps/spdlog/CMakeLists.txt)
add_subdirectory(deps/spdlog)
else()
# allow usage of installed dependency
find_package(spdlog ${SPDLOG_MIN_VERSION} REQUIRED)
add_library(${PROJECT_NAME}_spdlog INTERFACE IMPORTED)
endif()
I fails in the else statement. How can I fix this?
I found that OVERRIDE_FIND_PACKAGE does the job. If specifying this for spdlog, cmake uses this target in the cmake file of the spdlog_setup project.
I would like to build my project with OpenSSL from sources. I am using a modern CMake with FetchContent feature. So far, I have no trouble using FetchContent with CMake external projects, but OpenSSL does not use CMake.
My try so far:
FetchContent_Declare(
openssl
GIT_REPOSITORY https://github.com/openssl/openssl.git
GIT_TAG origin/master
CONFIGURE_COMMAND "./Configure"
BUILD_COMMAND "make"
TEST_COMMAND "make test"
)
...
FetchContent_MakeAvailable(openssl)
but this does not make anything in the main project and of course compilation fails for executables requiring lib openssl.
Could you please help me to figure out if it's possible to automatically build the openssl lib for my program ? I would like to avoid usage of existing non official wrappers of openssl with cmake.
Thanks a lot in advance
Stephane
I think that you are mixing FetchContent and ExternalProject.
FetchContent will download (and possibly patch) your subproject, typically to use it like a "git submodule", and ExternalProject will not only download it, but also build it. From the FetchContent docs:
In addition to the above explicit options, any other unrecognized options are passed through unmodified to ExternalProject_Add() to perform the download, patch and update steps. The following options are explicitly prohibited (they are disabled by the FetchContent_Populate() command):
CONFIGURE_COMMAND
BUILD_COMMAND
INSTALL_COMMAND
TEST_COMMAND
Now, there are two things with ExternalProject_Add():
It will run at build time. I like to use it as a cross-platform script to help users build the dependencies from source, but they have to run that script as a prerequisite, and then build my project by referencing to the result of this one.
It will build and install the project locally, so your main project that depends on it will have to find it, e.g. by pointing CMAKE_PREFIX_PATH to the install path of the dependency.
Also, you could make it such that the configure step of your main project runs ExternalProject_add in a separate process, but I think that it quickly makes it more complex and ends up not helping users so much.
All that to say that there is no silver bullet when it comes to dependencies, and for a dependency like OpenSSL, the simplest may be to give instructions explaining how to apt install openssl or brew install openssl, and then use find_package(OpenSSL REQUIRED) in your CMakeLists.txt!
This one is tricky, because OpenSSL does not use CMake, it has a custom build script, and has a complex behavior with glibc. As #JonasVautherin said, you cannot use FetchContent_Declare, you must use ExternalProject_Add:
set(OPENSSL_SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/openssl-src) # default path by CMake
set(OPENSSL_INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/openssl)
set(OPENSSL_INCLUDE_DIR ${OPENSSL_INSTALL_DIR}/include)
set(OPENSSL_CONFIGURE_COMMAND ${OPENSSL_SOURCE_DIR}/config)
ExternalProject_Add(
OpenSSL
SOURCE_DIR ${OPENSSL_SOURCE_DIR}
GIT_REPOSITORY https://github.com/openssl/openssl.git
GIT_TAG OpenSSL_1_1_1n
USES_TERMINAL_DOWNLOAD TRUE
CONFIGURE_COMMAND
${OPENSSL_CONFIGURE_COMMAND}
--prefix=${OPENSSL_INSTALL_DIR}
--openssldir=${OPENSSL_INSTALL_DIR}
BUILD_COMMAND make
TEST_COMMAND ""
INSTALL_COMMAND make install
INSTALL_DIR ${OPENSSL_INSTALL_DIR}
)
After doing that, you cannot include or link it with your other targets yet. To do that, you still need to declare the library, as if it was found using find_package, i.e. as if FindOpenSSL.cmake was used:
# We cannot use find_library because ExternalProject_Add() is performed at build time.
# And to please the property INTERFACE_INCLUDE_DIRECTORIES,
# we make the include directory in advance.
file(MAKE_DIRECTORY ${OPENSSL_INCLUDE_DIR})
add_library(OpenSSL::SSL STATIC IMPORTED GLOBAL)
set_property(TARGET OpenSSL::SSL PROPERTY IMPORTED_LOCATION ${OPENSSL_INSTALL_DIR}/lib/libssl.${OPENSSL_LIBRARY_SUFFIX})
set_property(TARGET OpenSSL::SSL PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${OPENSSL_INCLUDE_DIR})
add_dependencies(OpenSSL::SSL OpenSSL)
add_library(OpenSSL::Crypto STATIC IMPORTED GLOBAL)
set_property(TARGET OpenSSL::Crypto PROPERTY IMPORTED_LOCATION ${OPENSSL_INSTALL_DIR}/lib/libcrypto.${OPENSSL_LIBRARY_SUFFIX})
set_property(TARGET OpenSSL::Crypto PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${OPENSSL_INCLUDE_DIR})
add_dependencies(OpenSSL::Crypto OpenSSL)
Now you can include the OpenSSL and link with it normally.
Basically, I want to have my structure like such:
MainRepo
+---app1
+---app2
+---common
+---some_lib1
+---some_lib2
+---protobuf
+---comms.proto
+---cs
+---Comms.pb.cs
+---cpp
+---comms.pb.cc
+---comms.pb.h
I want to be able to check out the repo and have a script that runs protoc for all the different languages for the apps that are in the repo. This is a mono repo containing an app for two different arm machines and an x64. I essentially are running protoc and it generates all the source files for c, js, cs, cpp, etc and puts them under protobuf in their own folders.
I want to have app1, for example, go find the c++ header and source and use them to build the app. At the moment, the example I have been hacking, uses cmake to generate the .cc and .h which makes it inconvenient for me as intellisense complains since those files dont exist when I'm writing.
Anyway, I've been hacking away at cmake all day. I always end up with cmake having a forward declaration error and cant compile my .cc and .h
PROJECT(test)
CMAKE_MINIMUM_REQUIRED (VERSION 2.6)
SET(CMAKE_CXX_FLAGS "-g -Wall -Werror -std=c++11")
ADD_EXECUTABLE(main main.cpp)
TARGET_LINK_LIBRARIES(main proto ${PROTOBUF_LIBRARY})
find_package(Protobuf REQUIRED)
set(PROTOBUF_IMPORT_DIRS "../proto")
set (msgs ${PROTOBUF_IMPORT_DIRS}/communications.proto)
PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${msgs})
add_library(proto SHARED ${PROTO_SRCS})
target_link_libraries(proto ${PROTOBUF_LIBRARY})
Wondering if there are any suggestions. I dont want to have my protobuf stuff outside of my common folder and I also dont need protoc to generate those files either as I do that another way (although I could change that way). I just want to ensure that the language specific files are still available to view and not just generated during cmake and I cant view them.
My 2 cents,
Here this is what I did for google/or-tools
ref: https://github.com/google/or-tools
Protobuf Integration
I'm using Fetchcontent() (CMake >= 3.18 to have SOURCE_SUBDIR option IIRC), but I also need to patch it (e.g. to have CMP0077)
you can find the protobuf patch here: https://github.com/google/or-tools/blob/stable/patches/protobuf-v3.12.2.patch
cmake/dependencies/CMakeLists.txt
include(FetchContent)
set(BUILD_SHARED_LIBS OFF)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(BUILD_TESTING OFF)
message(CHECK_START "Fetching Protobuf")
list(APPEND CMAKE_MESSAGE_INDENT " ")
set(protobuf_BUILD_TESTS OFF)
set(protobuf_BUILD_EXPORT OFF)
set(protobuf_MSVC_STATIC_RUNTIME OFF)
# FetchContent_Declare(SOURCE_SUBDIR) was introduced in 3.18
FetchContent_Declare(
protobuf
GIT_REPOSITORY "https://github.com/protocolbuffers/protobuf.git"
GIT_TAG "v3.12.2"
PATCH_COMMAND git apply "${CMAKE_CURRENT_LIST_DIR}/../../patches/protobuf-v3.12.2.patch"
SOURCE_SUBDIR cmake)
FetchContent_MakeAvailable(protobuf)
list(POP_BACK CMAKE_MESSAGE_INDENT)
message(CHECK_PASS "fetched")
ref: https://github.com/google/or-tools/blob/a0a56698ba8fd07b7f84aee4fc45d891a8cd9828/cmake/dependencies/CMakeLists.txt#L142-L168
note: for cmake < 3.18, I use ExternalProject + execute_process()
see: https://github.com/google/or-tools/blob/a0a56698ba8fd07b7f84aee4fc45d891a8cd9828/cmake/utils.cmake#L66-L137
Generate Protobuf files
Since we have integrated Protobuf, now we have access to protobuf::protoc.
To build proto file, simply adapt
# Get Protobuf include dirs
get_target_property(protobuf_dirs protobuf::libprotobuf INTERFACE_INCLUDE_DIRECTORIES)
foreach(dir IN LISTS protobuf_dirs)
if ("${dir}" MATCHES "BUILD_INTERFACE")
message(STATUS "Adding proto path: ${dir}")
list(APPEND PROTO_DIRS "--proto_path=${dir}")
endif()
endforeach()
# Generate Protobuf cpp sources
set(PROTO_HDRS)
set(PROTO_SRCS)
file(GLOB_RECURSE proto_files RELATIVE ${PROJECT_SOURCE_DIR}
"common/protobuf/*.proto"
)
foreach(PROTO_FILE IN LISTS proto_files)
#message(STATUS "protoc proto(cc): ${PROTO_FILE}")
get_filename_component(PROTO_DIR ${PROTO_FILE} DIRECTORY)
get_filename_component(PROTO_NAME ${PROTO_FILE} NAME_WE)
set(PROTO_HDR ${PROJECT_BINARY_DIR}/${PROTO_DIR}/${PROTO_NAME}.pb.h)
set(PROTO_SRC ${PROJECT_BINARY_DIR}/${PROTO_DIR}/${PROTO_NAME}.pb.cc)
#message(STATUS "protoc hdr: ${PROTO_HDR}")
#message(STATUS "protoc src: ${PROTO_SRC}")
add_custom_command(
OUTPUT ${PROTO_SRC} ${PROTO_HDR}
COMMAND protobuf::protoc
"--proto_path=${PROJECT_SOURCE_DIR}"
${PROTO_DIRS}
"--cpp_out=${PROJECT_BINARY_DIR}"
${PROTO_FILE}
DEPENDS ${PROTO_FILE} protobuf::protoc
COMMENT "Generate C++ protocol buffer for ${PROTO_FILE}"
VERBATIM)
list(APPEND PROTO_HDRS ${PROTO_HDR})
list(APPEND PROTO_SRCS ${PROTO_SRC})
endforeach()
ref: https://github.com/google/or-tools/blob/a0a56698ba8fd07b7f84aee4fc45d891a8cd9828/cmake/cpp.cmake#L234-L279
After you can use PROTO_HDR and PROTO_SRC e.g. add them to your target sources etc...
note: for .Net you can take a look at
https://github.com/google/or-tools/blob/a0a56698ba8fd07b7f84aee4fc45d891a8cd9828/cmake/dotnet.cmake#L30-L60
All in all, just adapt to your need, e.g. generate in source tree than binary dir etc. and you should be able to do whatever you want...
I'm on cmake version 3.12.1 and want to build a static executable that uses ZLIB. I have both the static (libz.a) and shared (libz.so) libraries on my machine. How can I tell find_package(ZLIB) to return the static version? Maybe there's another way to find libz.a as well?
My present workaround is to specify:
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
Then:
target_link_libraries (my_binary z lib1 lib2)
Critique on this approach is also welcome!
As of CMake 3.24, use: set(ZLIB_USE_STATIC_LIBS "ON")
Source
Your approach is valid given the limitations of the CMake module called by find_package(ZLIB), specifically FindZLIB.cmake. While other FindXXX.cmake modules have a special option for grabbing static libraries, the zlib module does not.
There are already a few questions on SO about this topic, but some are older than others, so there are a few options.
You can instead apply the -static flag on a more granular level (rather than editing the global CMAKE_EXE_LINKER_FLAGS variable) by adding it to your target_link_libraries call. This way it will apply only to that target -- useful if you are building other non-static targets.
You could also tell CMake to search for static libraries explicitly by setting CMAKE_FIND_LIBRARY_SUFFIXES. When find_package is called, CMake can search for libraries ending in .a using this:
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
find_package(ZLIB REQUIRED)
If you have control over installing zlib, for example, you are installing dependencies in a Continuous Integration setup, I would recommend to just remove the zlib dynamic library.
zlib doesn't have the option to build statically or dynamically, it automatically generates both versions. However FindZlib.cmake prioritizes the dynamic version.
I find the following approach to be better in case you don't have access to modify third parties repositories CMakeLists.txt that needs zlib:
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(_compiler_is_msvc ON)
endif()
option(ZLIB_FORCE_STATIC "Remove the dynamic libraries after zlib install" ON)
mark_as_advanced(ZLIB_FORCE_STATIC)
set(OUTPUT_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR} CACHE PATH "Base folder where builds and source folder will be installed: i.e. OUTPUT_BUILD_DIR/zlib")
if(_compiler_is_msvc)
set(ZLIB_GIT_TAG cacf7f1d4e3d44d871b605da3b647f07d718623f) # Version 1.2.11
message(STATUS "ZLIB_VERSION: ${ZLIB_GIT_TAG} : Version 1.2.11")
set(ZLIB_BUILD_DIR ${OUTPUT_BUILD_DIR}/zlib-build)
set(ZLIB_INSTALL_DIR ${OUTPUT_BUILD_DIR}/zlib)
set(ZLIB_SRC_FOLDER_NAME zlib-src)
set(ZLIB_SRC_DIR ${OUTPUT_BUILD_DIR}/${ZLIB_SRC_FOLDER_NAME})
set(ZLIB_GIT_REPOSITORY "https://github.com/madler/zlib")
ExternalProject_Add(ep_zlib
GIT_REPOSITORY ${ZLIB_GIT_REPOSITORY}
GIT_TAG ${ZLIB_GIT_TAG}
# GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
CMAKE_GENERATOR ${CMAKE_GENERATOR}
SOURCE_DIR ${ZLIB_SRC_DIR}
BINARY_DIR ${ZLIB_BUILD_DIR}
CMAKE_ARGS
-DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER}
-DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER}
-DCMAKE_BUILD_TYPE:STRING=${SGEXT_CMAKE_BUILD_TYPE}
-DBUILD_SHARED_LIBS:BOOL=OFF
-DCMAKE_INSTALL_PREFIX=${ZLIB_INSTALL_DIR}
)
if(ZLIB_FORCE_STATIC)
ExternalProject_Add_Step(
ep_zlib zlib_remove_dll
COMMENT "Remove zlib.lib and zlib.dll, leaves only zlibstatic.lib"
DEPENDEES install
COMMAND ${CMAKE_COMMAND} -E remove -f ${ZLIB_INSTALL_DIR}/lib/zlib.lib ${ZLIB_INSTALL_DIR}/bin/zlib.dll
)
endif()
endif()
The last step removes the dynamic version, so the default FindZLIB will find the static library.
The best solution I found was to name the library explicitly when calling CMake:
cmake -DZLIB_LIBRARY=/usr/lib/x86_64-linux-gnu/libz.a /path/to/source
I would not recommend the solution proposed by #phcerdan because in my case the installed shared library was colliding with an already installed version, so the only solution was to make sure it never gets installed in the first place. The key idea is to disable completely the targets installation using SKIP_INSTALL_LIBRARIES, and instead to "install" the static library manually. Nonetheless, my solution is quite similar:
EXTERNALPROJECT_ADD(zlib_external
GIT_REPOSITORY https://github.com/madler/zlib.git
GIT_TAG v1.2.11
CMAKE_ARGS
-DSKIP_INSTALL_FILES=ON # Disable install of manual and pkgconfig files
-DSKIP_INSTALL_LIBRARIES=ON # Do not install libraries automatically. It will be handled manually to avoid installing shared libs
-DBUILD_SHARED_LIBS=OFF
-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}
-DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX}
-DCMAKE_C_FLAGS:STRING=${CMAKE_COMPILE_FLAGS_EXTERNAL}
${EXTERNALPROJECT_BUILD_TYPE_CMD}
INSTALL_DIR ${CMAKE_INSTALL_PREFIX}
)
if(NOT WIN32)
set(zlib_BUILD_LIB_PATH "<BINARY_DIR>/libz.a")
set(zlib_PATH "${CMAKE_INSTALL_PREFIX}/lib/libz.a")
else()
set(zlib_BUILD_LIB_PATH "<BINARY_DIR>/Release/zlibstatic.lib")
set(zlib_PATH "${CMAKE_INSTALL_PREFIX}/lib/zlibstatic.lib")
endif()
ExternalProject_Add_Step(
zlib_external zlib_install_static_only
COMMENT "Manually installing only static library"
DEPENDEES install
COMMAND ${CMAKE_COMMAND} -E copy ${zlib_BUILD_LIB_PATH} ${zlib_PATH}
)
CMAKE : I only want to build part of a downloadable external project
The external project I want to use has the following structure :
- ExternalProject
- Subproject A <- this i care for
- Subproject B
It's all in the same archive that is available for download. The problem is that Subproject B has more dependencies that I don't want in my project, also it's not relevant for what I'm doing. The subprojects are buildable on their own, so for now I just took Subproject A out of the archive and put it in my main project which is working fine, but I'd like to not deploy the external project with my project but allow the user to download the external project on his own when running cmake. Sadly there is no archive for each of the subprojects on their own so all I can download is the full external project.
What I want is to tell CMAKE to download and unpack the whole archive ExternalProject but then only add Subproject A to my project. I read all the documentation on ExternalProject_add but it mostly allows for detailed configuration of the build parameters of the project. Maybe I'm just looking for the wrong keyword and my question is really simple to answer - or it is just not possible.
If someone could point me towards the right approach here I'd be very thankful.
Actual project :
The mentioned archive has two subfolders "octomap" and "octovis". octomap is the one I want to build while octovis will create a lot of errors if the dependencies are not met.
ExternalProject_Add(octomap-1.6.5
URL https://github.com/OctoMap/octomap/archive/v1.6.5.tar.gz
URL_MD5 de09b1189a03ac8cbe4f813951e86605
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/octomap/
CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${EXTERNAL_INSTALL_DIR}"
)
You can define all commands explicitly. For instance you have a directory structure:
FooBar/
- CMakeLists.txt
- Foo/
- - CMakeLists.txt # You wanna build
- Bar/
- - CMakeLists.txt # You wanna ignore
Example with hardcoded paths:
set(ext_dir "${CMAKE_BINARY_DIR}/third-party-activities/ExternalProject/")
set(foobar_dir "${ext_dir}/FooBar")
ExternalProject_Add(
FooBar
URL "your-url-here"
SOURCE_DIR "${foobar_dir}/Source"
CONFIGURE_COMMAND
"${CMAKE_COMMAND}"
"-H${foobar_dir}/Source/Foo"
"-B${foobar_dir}/Builds"
"-DCMAKE_INSTALL_PREFIX=${EXTERNAL_INSTALL_DIR}"
BUILD_COMMAND
"${CMAKE_COMMAND}" --build "${foobar_dir}/Builds"
INSTALL_COMMAND
"${CMAKE_COMMAND}" --build "${foobar_dir}/Builds" --target install
)
Update
Note that it's much easier to patch parent project like this:
# FooBar/CMakeLists.txt
option(BUILD_BAR_SUBPROJECT "Build targets from subproject Bar" ON)
...
add_subdirectory(Foo)
if(BUILD_BAR_SUBPROJECT)
add_subdirectory(Bar)
endif()
... and now you don't need to hack ExternalProject_Add so much:
ExternalProject_Add(
FooBar
URL "your-url-here"
CMAKE_ARGS -DBUILD_BAR_SUBPROJECT=OFF "-DCMAKE_INSTALL_PREFIX=${EXTERNAL_INSTALL_DIR}"
)