I would also recommend FetchContent for simple projects.
However, sometimes more control is needed over the library being downloaded.
For that ExternalProject is useful as it has many customization options.
The secret is understanding the library you are trying to download. Read its CMakeLists.txt file to understand how you can modify installation directories, or what configuration is needed previous to build or installation.
The following example will download ZeroMQ (A.K.A. zmq)
This library needs to run a bash script inside the library to configure things correctly and it has some options that can be used to customize your installation.
I personally don't like the standard locations where files are saved, as well as not needing to install the library in my system. So I want to install it within my project. Therefore you can specify the directories however you want.
In my project I override CMAKE_INSTALL_PREFIX and CMAKE_INCLUDE_PREFIX to point to the folders I desire the files to be.
set(ZMQ_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}")
set(ZMQ_DIR "${CMAKE_CURRENT_BINARY_DIR}/ZeroMQ")
set(ZMQ_SRC_DIR "${ZMQ_DIR}/src")
set(ZMQ_INSTALL_INCLUDEDIR "${CMAKE_INCLUDE_PREFIX}/ZeroMQ")
include(ExternalProject)
ExternalProject_Add(ZeroMQ
URL https://github.com/zeromq/libzmq/releases/download/v4.3.4/zeromq-4.3.4.tar.gz
PREFIX "${ZMQ_DIR}/prefix"
TMP_DIR "${ZMQ_DIR}/tmp"
STAMP_DIR "${ZMQ_DIR}/stamp"
DOWNLOAD_DIR "${ZMQ_DIR}"
SOURCE_DIR "${ZMQ_SRC_DIR}"
BINARY_DIR "${ZMQ_DIR}/build"
CONFIGURE_COMMAND cd ${ZMQ_SRC_DIR} && ./configure --with-pic --prefix=${ZMQ_INSTALL_DIR}
BUILD_COMMAND ""
INSTALL_COMMAND cd ${ZMQ_SRC_DIR} && $(MAKE) install
)
Note that ZeroMQ is not the actual library target, it is a name I chose for the ExternalProject target. The library target would be zmq. So the library file would be libzmq.so.
After build or installation your main target needs to show it depends on the external library so that things are built in the right order.
Therefor, you would need to add the following in the CMakeLists.txt where you target is (or in any parent CMakeLists.txt):
include_directories("${CMAKE_INCLUDE_PREFIX}/ZeroMQ")
add_dependencies(your_target ZeroMQ) # Depends on the ExternalProject
target_link_libraries(your_target zmq) # Link the library target
If you need to download another external library that depends on zmq you can add a DEPENDS argument to ExternalProject_Add:
ExternalProject_Add(
cppzmqDownload
URL https://github.com/zeromq/cppzmq/archive/refs/tags/v4.10.0.tar.gz
DEPENDS ZeroMQ
CMAKE_ARGS
-DCMAKE_MODULE_PATH:PATH=${CMAKE_INSTALL_PREFIX}
-DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX}
-DCMAKE_INSTALL_INCLUDEDIR:PATH=${CMAKE_INCLUDE_PREFIX}/cppzmq
-DCPPZMQ_BUILD_TESTS=OFF
-DPC_LIBZMQ_INCLUDE_DIRS=${ZMQ_INSTALL_INCLUDEDIR}
-DPC_LIBZMQ_LIBRARY_DIRS=${CMAKE_LIB_PREFIX}
)
I hope this helps someone even though this is an old post.