Passing variables and their values from one ExternalProject dependency to another using cmake (in the context of zlib and libpng) - cmake

I am still at the beginning of my journey regarding ExternalProject. I have two projects used by my main project as dependencies: libpng and zlib. The former depends on the latter.
Inside the official CMakeLists.txt of libpng I see that there is the option PNG_BUILD_ZLIB, which you have to disable if you want to use a custom version of zlib. Currently my two externals look like this:
ExternalProject_Add(zlib
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/deps/zlib
INSTALL_DIR ${CMAKE_INSTALL_PREFIX}
DOWNLOAD_DIR ""
TMP_DIR ${DEPS_TMP}/zlib
STAMP_DIR ${DEPS_STAMP}/zlib
LOG_DIR ${DEPS_LOG}/zlib
BINARY_DIR ${DEPS_BUILD}/zlib
SOURCE_DIR ${DEPS}/zlib
INSTALL_COMMAND "${CMAKE_COMMAND}" -E echo "Skipping install step for dependency zlib"
INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}
BUILD_ALWAYS OFF
)
# Set zlib variables required by libpng
ExternalProject_Get_property(zlib BINARY_DIR)
ExternalProject_Get_property(zlib SOURCE_DIR)
set(zlib_DIR "${BINARY_DIR}/Debug" CACHE PATH "zlib dir" FORCE)
set(ZLIB_ROOT ${SOURCE_DIR} CACHE PATH "zlib root" FORCE) # ${zlib_DIR}
set(ZLIB_INCLUDE_DIRS ${SOURCE_DIR} CACHE PATH "zlib include dir" FORCE)
if(WIN32)
set(ZLIB_LIBRARY ${zlib_DIR}/zlibd.dll CACHE FILEPATH "zlib dynamic library" FORCE)
set(ZLIB_LIBRARIES ${zlib_DIR}/zlibd.dll CACHE FILEPATH "zlib dynamic library" FORCE)
else()
set(ZLIB_LIBRARY ${zlib_DIR}/libzlibd.so CACHE FILEPATH "zlib dynamic library" FORCE)
set(ZLIB_LIBRARIES ${zlib_DIR}/libzlibd.so CACHE FILEPATH "zlib dynamic library" FORCE)
endif()
ExternalProject_Add(libpng
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/deps/png
DEPENDS zlib
INSTALL_DIR ${CMAKE_INSTALL_PREFIX}
DOWNLOAD_DIR ""
TMP_DIR ${DEPS_TMP}/png
STAMP_DIR ${DEPS_STAMP}/png
LOG_DIR ${DEPS_LOG}/png
BINARY_DIR ${DEPS_BUILD}/png
SOURCE_DIR ${DEPS}/libpng
INSTALL_COMMAND "${CMAKE_COMMAND}" -E echo "Skipping install step for dependency libpng"
INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}
BUILD_ALWAYS OFF
CMAKE_ARGS
"-DPNG_BUILD_ZLIB=1" # Use zlib built as a dependency of this project
"-DPNG_SHARED=1"
"-DPNG_STATIC=0"
"-DPNG_TESTS=0"
"-DPNG_EXECUTABLES=0"
"-DPNG_HARDWARE_OPTIMIZATIONS=1"
"-DPNG_DEBUG=0"
)
where
${DEPS} points at a directory where currently every dependency's source code resides and is manually downloaded using git submodules commands
${DEPS_xyz} points at a ExternalProject directory for a specific purpose (logs, stamps, build files, temporary files etc.) and all are placed inside the build directory of the whole project.
The problem I am having is that I am failing at passing the include directory to libpng and this results in the header zlib.h not being found during the build step for that dependency. Apparently
set(ZLIB_INCLUDE_DIRS ${SOURCE_DIR} CACHE PATH "zlib include dir" FORCE)
isn't really passed to the dependency (but in itself it is pointing at the correct path on the filesystem where the header file is included).
Do I have to add the include directory as a CMAKE_ARGS entry or is there some other easier way?
I also checked here but the suggested solution (I just passed the stuff directly into my CMAKE_ARGS)
CMAKE_ARGS
"-DPNG_BUILD_ZLIB=1" # Use zlib built as a dependency of this project
"-DZLIB_INCLUDE_DIRS=\"${${ZLIB_INCLUDE_DIRS}}\"" # Pass the ZLIB_INCLUDE_DIRS to the dependency using the value of the variable set above
"-DPNG_SHARED=1"
"-DPNG_STATIC=0"
"-DPNG_TESTS=0"
"-DPNG_EXECUTABLES=0"
"-DPNG_HARDWARE_OPTIMIZATIONS=1"
"-DPNG_DEBUG=0"
doesn't work.

Related

put external project into a cPack

I try to setup a cMake project which create a package containing the output of multiple external projects. So the idea is, that I use this cMake project to create packages for the deployment.
Now the problem is, that when I use the cMakeLists shown bellow, the output of the external project is not included in the package. Just the internal one is in the package.
The install sequence of the external project seems to work, at least the output is in the install directory. It seams, that the package command doesn't see the binaries and for that reason dosn't put them into the zip archive.
Does anyone know, how to put the output of the external project into the package?
By the way: the external project is also cMake based. It contains a install step but no package step.
cmake_minimum_required (VERSION 3.12)
project(PackedBinaries
VERSION 0.0.42
)
set(GLOBAL_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/temp)
include(ExternalProject)
# Builds external projects.
ExternalProject_Add(
myExternalProject
GIT_REPOSITORY "..."
GIT_TAG "master"
UPDATE_COMMAND ""
PATCH_COMMAND ""
SOURCE_DIR "${PROJECT_BINARY_DIR}/repo"
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${GLOBAL_OUTPUT_PATH}
TEST_COMMAND ""
)
# Additional executable
add_executable(myInternalProject hello.c)
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE PATH "..." FORCE)
set(CPACK_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE PATH "..." FORCE)
endif()
install(TARGETS myInternalProject)
install(DIRECTORY ${CMAKE_SOURCE_DIR}/temp DESTINATION "${CMAKE_BINARY_DIR}/install")
set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
set(CPACK_PACKAGE_VERSION_PATCH "0")
set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY ${PROJECT_NAME})
set(CPACK_GENERATOR "ZIP")
# Must be after the last CPACK macros
include(CPack)

CMakeLists.txt not in root directory, can't do an automatic build

I have an issue with a repository not having its CMakeLists in the root directory, namely https://github.com/lz4/lz4
The CMakeLists.txt is in the subfolder contrib/cmake_unofficial.
I already checked similar questions on SO (Is it possible to have cmake build file (CMakeLists.txt) not in root in CLion, cmake - CMakeLists.txt is not in root folder (but is included in source)), but they only provide alternatives, and not a solution applicable to my situation.
Heres the cmake module I came up with:
if(ENABLE_LZ4)
message(STATUS "Using LZ4.")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DENABLE_LZ4")
# Enable ExternalProject CMake module
include(ExternalProject)
set(LZ4_SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/lz4)
set(LZ4_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/lib)
# Download and install lz4
ExternalProject_Add(
lz4
GIT_REPOSITORY https://github.com/lz4/lz4.git
GIT_TAG dev
SOURCE_DIR ${LZ4_SOURCE_DIR}
BINARY_DIR ${LZ4_BINARY_DIR}
INSTALL_COMMAND ""
CMAKE_ARGS
${LZ4_SOURCE_DIR}/contrib/cmake_unofficial
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
)
# Get lz4 source and binary directories from CMake project
ExternalProject_Get_Property(lz4 source_dir binary_dir)
# Create a liblz4 target to be used as a dependency by the program
add_library(liblz4 IMPORTED SHARED GLOBAL)
add_dependencies(liblz4 lz4)
include_directories(
${LZ4_SOURCE_DIR}/lib
)
set(LZ4_LIB ${LZ4_BINARY_DIR}/liblz4.so)
else()
message(STATUS "Not using LZ4.")
set(LZ4_LIB "")
endif()
Here the complete error output:
[ 0%] Performing update step for 'lz4'
Current branch dev is up to date.
[ 1%] Performing configure step for 'lz4'
CMake Error: The source directory "/****/build/lz4" does not appear to contain CMakeLists.txt.
Specify --help for usage, or press the help button on the CMake GUI.
CMakeFiles/lz4.dir/build.make:105: recipe for target 'lz4-prefix/src/lz4-stamp/lz4-configure' failed
make[2]: *** [lz4-prefix/src/lz4-stamp/lz4-configure] Error 1
CMakeFiles/Makefile2:72: recipe for target 'CMakeFiles/lz4.dir/all' failed
make[1]: *** [CMakeFiles/lz4.dir/all] Error 2
Makefile:94: recipe for target 'all' failed
make: *** [all] Error 2
I tried adding the path 'contrib/cmake_unofficial' to the CMAKE_ARGS variable (as seen in the module above), but it does not work (seems to be ignored?).
I also tried using PATCH_COMMAND to copy the CMakeLists.txt to the root before the build starts, but the relative paths of the file get messed up.
In other words, i need the cmake command to be called to build the library to be : cmake contrib/cmake_unofficial.
I also tried using CONFIGURE_COMMAND for this, but keep getting a file not found error for some reason (even though the path is correct).
The module has some other issues too, but I'm only interested in the non-root CMakeLists.
Thanks in advance!
ExternalProject separates download and source directories:
DOWNLOAD_DIR - a directory where downloading step is performed
SOURCE_DIR - a directory used as a source one when configuration step is performed
When use git for extract the project, note that git clone is called from the download directory, where it creates new directory with a project sources.
set(LZ4_DOWNLOAD_DIR ${CMAKE_CURRENT_BINARY_DIR}/lz4)
# Set a source dir based on the download one.
set(LZ4_SOURCE_DIR ${LZ4_DOWNLOAD_DIR}/lz4/contrib/cmake_unofficial)
set(LZ4_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/lib)
# Download and install lz4
ExternalProject_Add(
lz4
DOWNLOAD_DIR ${LZ4_DOWNLOAD_DIR} # Set download directory explicitely
GIT_REPOSITORY https://github.com/lz4/lz4.git
GIT_TAG dev
SOURCE_DIR ${LZ4_SOURCE_DIR}
BINARY_DIR ${LZ4_BINARY_DIR}
...
)
Probably not the correct way to do it, but this seems to work for me.
I used the CONFIGURE_COMMAND to call cmake on the correct directory.
Then use BUILD_COMMAND to call make
So essentially, it breaks down to this:
ExternalProject_Add(
lz4
GIT_REPOSITORY https://github.com/lz4/lz4.git
GIT_TAG dev
SOURCE_DIR ${LZ4_SOURCE_DIR}
BINARY_DIR ${LZ4_BINARY_DIR}
CONFIGURE_COMMAND cmake ${LZ4_SOURCE_DIR}/contrib/cmake_unofficial
BUILD_COMMAND make
INSTALL_COMMAND ""
CMAKE_ARGS
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
)

cmake's ExternalProject_Add: how to set source, build and install directories

I want to use ExternalProject_Add function to install cmake enabled projects. I need to control the building and installation processes. More specifically, I want that cmake build and install in specific directories.
There is an option to select a path where to put the source directory: SOURCE_DIR. Is there something equivalent for BUILD_DIR and INSTALL_DIR? I did not see anything alike.
There is a PREFIX option:
Root dir for entire project
What does it mean exactly? An how does cmake's ExternalProject_Add do the installation?
Edit:
This work, but I have no control over source directory and it does not install the library:
ExternalProject_Add(
wjelement-project
GIT_REPOSITORY "https://github.com/netmail-open/wjelement.git"
GIT_TAG "v1.2"
UPDATE_COMMAND ""
PATCH_COMMAND ""
TEST_COMMAND ""
INSTALL_COMMAND ""
#SOURCE_DIR "${MY_SOURCE_ROOT}/wjelement"
BINARY_DIR "${MY_BUILD_DIR}/wjelement"
INSTALL_DIR "${CCT_INSTALL_DIR}/wjelement"
)
If I uncomment SOURCE_DIR, it does not clone from GIT_CLONE but try to get source from SOURCE_DIR (and it fails because that's not what I expected...)
If I comment INSTALL_COMMAND "", then it try to install in C:/Program Files/wjelement, the default (apparently) and not in INSTALL_DIR
default path for source files seem to be ${CMAKE_BINARY_DIR}/wjelement-project-prefix/src
Another Stackoverflow question report problem (or at least non intuitive behavior) with INSTALL_DIR.

CMake External Project not Downloading

I am using CMake 3.5.1 with CLion, and I am attempting to download and install an external project from a URL. My CMakeLists.txt include:
include(ExternalProject)
set(EXTERNAL ${PROJECT_SOURCE_DIR}/external)
ExternalProject_Add(eigen_test
PREFIX ${EXTERNAL}/eigen
DOWNLOAD_DIR ${EXTERNAL}/eigen/download
SOURCE_DIR ${EXTERNAL}/eigen/src
BINARY_DIR ${EXTERNAL}/eigen/build
INSTALL_DIR ${EXTERNAL}/eigen/install
URL http://bitbucket.org/eigen/eigen/get/3.2.4.tar.gz
URL_MD5 4d0d77e06fef87b4fcd2c9b72cc8dc55
CONFIGURE_COMMAND cd <BINARY_DIR> && cmake -D CMAKE_INSTALL_PREFIX=$<INSTALL_DIR> <SOURCE_DIR>
)
When building, this directory structure is is created inside ${PROJECT_SOURCE_DIR}/external/
Although the cmake files exist to download and extract the project, these actions never occur. What am I doing wrong here?
I don't see a reason to pollute your project by downloading and building the library in ${PROJECT_SOURCE_DIR}, I always do that in ${CMAKE_BINARY_DIR}.
The eigen library uses CMakeLists.txt, it will work seamlessly as external project, you don't have to extensively configure each step. So CONFIGURE_COMMAND can be removed and the install prefix set with CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=...
This is the simplified and working configuration:
include(ExternalProject)
set(EXTERNALS_DIR ${CMAKE_BINARY_DIR}/external)
ExternalProject_Add(eigen_test
PREFIX ${EXTERNALS_DIR}
URL http://bitbucket.org/eigen/eigen/get/3.2.4.tar.gz
URL_MD5 4d0d77e06fef87b4fcd2c9b72cc8dc55
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTERNALS_DIR}/installed
)
How to use the library:
Add ${EXTERNALS_DIR}/installed/include to include directories
Build the eigen_test target

How to download a toolchain for cross compilation in cmake from separate file?

I have a project with a CMakeLists.txt files in the root and the project compiles fine on Linux and OSX. Now I want to cross compile it for MIPS OpenWRT.
I would like to automate it as much as possible, so I would use following code to download the toolchain and set the compiler variables:
ExternalProject_Add(ar71xx-toolchain
PREFIX "${PROJECT_BINARY_DIR}/external/openwrt"
URL "http://downloads.openwrt.org/barrier_breaker/14.07/ar71xx/generic/OpenWrt-Toolchain-ar71xx-for-mips_34kc-gcc-4.8-linaro_uClibc-0.9.33.2.tar.bz2"
UPDATE_COMMAND ""
PATCH_COMMAND ""
BUILD_COMMAND ""
CONFIGURE_COMMAND ""
INSTALL_COMMAND ""
)
ExternalProject_Get_Property(ar71xx-toolchain SOURCE_DIR)
SET(CMAKE_C_COMPILER ${SOURCE_DIR}/toolchain-mips_34kc_gcc-4.8-linaro_uClibc-0.9.33.2/bin/mips-openwrt-linux-gcc)
SET(CMAKE_CXX_COMPILER ${SOURCE_DIR}/toolchain-mips_34kc_gcc-4.8-linaro_uClibc-0.9.33.2/bin/mips-openwrt-linux-g++)
SET(CMAKE_STRIP ${SOURCE_DIR}/toolchain-mips_34kc_gcc-4.8-linaro_uClibc-0.9.33.2/bin/mips-openwrt-linux-strip)
I thought that I can put it in a separate toolchain file and pass it with -DCMAKE_TOOLCHAIN_FILE, but it seems that ExternalProject_Add is not executed inside the toolchain file.
I would like to avoid putting the toolchain download step into the main CMakeLists.txt since it's actually not essential for the project itself and would require doing the same for each target platform...
So is there a way to define optional steps for a current cross compile build and pass it somehow as command line parameter to be executed before the main project build?
UPDATE:
Based on Tsyvarev's answer that works for me in the toolchain file:
set(CMAKE_SYSTEM_NAME Linux)
set(TOOLCHAIN_DIR ${PROJECT_BINARY_DIR}/external/openwrt/toolchain)
if(NOT EXISTS ${TOOLCHAIN_DIR})
file(DOWNLOAD http://downloads.openwrt.org/barrier_breaker/14.07/ar71xx/generic/OpenWrt-Toolchain-ar71xx-for-mips_34kc-gcc-4.8-linaro_uClibc-0.9.33.2.tar.bz2 ${TOOLCHAIN_DIR}/toolchain.tar.bz2 SHOW_PROGRESS)
execute_process(COMMAND tar --strip-components=2 -xjf ${TOOLCHAIN_DIR}/toolchain.tar.bz2 WORKING_DIRECTORY ${TOOLCHAIN_DIR})
execute_process(COMMAND rm ${TOOLCHAIN_DIR}/toolchain.tar.bz2)
endif()
SET(CMAKE_C_COMPILER ${TOOLCHAIN_DIR}/bin/mips-openwrt-linux-gcc)
SET(CMAKE_CXX_COMPILER ${TOOLCHAIN_DIR}/bin/mips-openwrt-linux-g++)
SET(CMAKE_STRIP ${TOOLCHAIN_DIR}/bin/mips-openwrt-linux-strip)
SET(CMAKE_FIND_ROOT_PATH ${TOOLCHAIN_DIR})
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
There is one issue when passing -DCMAKE_TOOLCHAIN_FILE as CMAKE parameter to other projects added with ExternalProject_Add. Because of it's own ${PROJECT_BINARY_DIR} it will download the toolchain again. But this is another problem...
ExternalProject_add executes all steps at build time, not at configuration time.
For download file you can use file(DOWNLOAD ...) command. For extract files from archive just use execute_process with appropriate command.