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

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}
)

Related

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

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.

Set WORKING_DIRECTORY for ExternalProject_Add CONFIGURE_COMMAND

I'm trying to add an Automake project as an external project.
The Automake configure script (usually run with ./configure) contains a relative path to a resource file. The file is found when I run configure manually, because my working directory is in the source directory. However, when I run configure with the ExternalProject_Add, it can't find the resource file because the working directory is CMAKE_CURRENT_BINARY_DIR.
ExternalProject_Add(zlib
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}
CONFIGURE_COMMAND sh ${CMAKE_CURRENT_SOURCE_DIR}/configure
BUILD_COMMAND make)
How can I set the working directory for the configuration step so that the config script finds the required files?
In ExternalProject_Add configuration step is performed with build directory (BINARY_DIR option) being current, so you may set this option:
ExternalProject_Add(...
BINARY_DIR <dir>
...
)
For in-source builds (when build directory is the same as source directory), BUILD_IN_SOURCE option could be used as alternative to set BINARY_DIR option:
ExternalProject_Add(...
BUILD_IN_SOURCE 1
...
)
More info see at ExternalProject documentation page.

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 reuse extracted source with CMake's ExternalProject_Add?

I use the following CMake file to automatically download and compile the Boost libraries. After generating project files for my compiler, I run cmake --build . --config Debug and cmake --build . --config Release to build both variants. So I envoke the build process twice. Even though CMake is clever enough to not download the archive again if the checksum still matches, it extracts it into the source directory twice. Extraction takes a couple of minutes for the large Boost libraries, so I'd like to prevent CMake from doing it for the second build.
ExternalProject_Add(Boost
PREFIX ${BOOST_PREFIX}
TMP_DIR ${BOOST_PREFIX}/temp
STAMP_DIR ${BOOST_PREFIX}/stamp
#--Download step--------------
DOWNLOAD_DIR ${BOOST_PREFIX}/download
URL http://downloads.sourceforge.net/project/boost/boost/1.56.0/boost_1_56_0.tar.gz
URL_MD5 8c54705c424513fa2be0042696a3a162
#--Update/Patch step----------
UPDATE_COMMAND ""
#--Configure step-------------
SOURCE_DIR ${BOOST_PREFIX}/source
CONFIGURE_COMMAND ${BOOST_CONFIGURE_COMMAND}
#--Build step-----------------
BUILD_COMMAND ${BOOST_BUILD_EXECUTABLE} install
--build-dir=${BOOST_PREFIX}/build
--prefix=${BOOST_PREFIX}/install
variant=${BOOST_VARIANT}
link=${BOOST_LINK}
threading=multi
address-model=32
toolset=${BOOST_TOOLSET}
runtime-link=${BOOST_RUNTIME_LINK}
BUILD_IN_SOURCE 1
#--Install step---------------
INSTALL_COMMAND ""
)
In this particually example it would possible to just use variant=debug,release, but there are some other external dependencies in my project that I build this way. How can I make CMake only extract the archive if it downloaded a new one?
Create two projects: Foo-Release and Foo-Debug. Leave DOWNLOAD_COMMAND empty for the second project and make it depends on the first. Set SOURCE_DIR explicilty for both projects (point to the same directory). I.e.:
set(source_dir "${CMAKE_CURRENT_BINARY_DIR}/3rdParty/Foo/Source")
ExternalProject_Add(
Foo-Release
URL "..."
URL_HASH SHA1=...
SOURCE_DIR "${source_dir}"
...
)
ExternalProject_Add(
Foo-Debug
DOWNLOAD_COMMAND
""
SOURCE_DIR "${source_dir}"
...
)
add_dependencies(Foo-Debug Foo-Release)
Example