How to propagate -Wno-dev to cmake using FetchContent_Declare? - cmake

I am using the FetchContent feature from CMake (3.12) and declaring it like this:
FetchContent_Declare(libsndfile
GIT_REPOSITORY ${LIBSNDFILE_GIT_REPO}
GIT_TAG ${LIBSNDFILE_GIT_TAG}
GIT_CONFIG advice.detachedHead=false
SOURCE_DIR "${CMAKE_BINARY_DIR}/libsndfile"
BINARY_DIR "${CMAKE_BINARY_DIR}/libsndfile-build"
CMAKE_ARGS "-Wno-dev"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)
According to the CMake documentation:
FetchContent_Declare: The <contentOptions> can be any of the download or update/patch options that the ExternalProject_Add() command understands
And according to the ExternalProject_Add documentation, "The specified arguments are passed to the cmake command line" when using CMAKE_ARGS.
The -Wno-dev option does not seem to be passed along as I continue to see this warning messages in the output:
CMake Warning (dev) at /Volumes/Vault/misc/src/libsndfile/CMakeLists.txt:446 (add_executable):
Policy CMP0063 is not set: Honor visibility properties for all target
types. Run "cmake --help-policy CMP0063" for policy details. Use the
cmake_policy command to set the policy and suppress this warning.
Target "sndfile-interleave" of type "EXECUTABLE" has the following
visibility properties set for C:
C_VISIBILITY_PRESET
For compatibility CMake is not honoring them for this target.
This warning is for project developers. Use -Wno-dev to suppress it.
I believe I am following the documentation but it seems I must be doing something wrong. Any idea what could be wrong?
Edit: As requested in comment, here is a complete example:
File CMakeLists.txt
cmake_minimum_required(VERSION 3.12)
project(self_contained_libsndfile_example)
set(CMAKE_CXX_STANDARD 14)
# This is in order to trigger the warnings in FetchContent
set(CMAKE_C_VISIBILITY_PRESET hidden)
include(FetchContent)
set(LIBSNDFILE_GIT_REPO "https://github.com/erikd/libsndfile" CACHE STRING "libsndfile git repository url" FORCE)
set(LIBSNDFILE_GIT_TAG b4bd397ca74f4c72b9cabaae66fef0c3d5a8c527 CACHE STRING "libsndfile git tag" FORCE)
FetchContent_Declare(libsndfile
GIT_REPOSITORY ${LIBSNDFILE_GIT_REPO}
GIT_TAG ${LIBSNDFILE_GIT_TAG}
GIT_CONFIG advice.detachedHead=false
SOURCE_DIR "${CMAKE_BINARY_DIR}/libsndfile"
BINARY_DIR "${CMAKE_BINARY_DIR}/libsndfile-build"
CMAKE_ARGS "-Wno-dev"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)
FetchContent_GetProperties(libsndfile)
if(NOT libsndfile_POPULATED)
FetchContent_Populate(libsndfile)
endif()
set(LIBSNDFILE_ROOT_DIR ${libsndfile_SOURCE_DIR})
set(LIBSNDFILE_INCLUDE_DIR "${libsndfile_BINARY_DIR}/src")
add_subdirectory(${libsndfile_SOURCE_DIR} ${libsndfile_BINARY_DIR} EXCLUDE_FROM_ALL)
file(COPY "${libsndfile_SOURCE_DIR}/src/sndfile.hh" DESTINATION ${LIBSNDFILE_INCLUDE_DIR})
include_directories(${LIBSNDFILE_INCLUDE_DIR})
set(target self_contained_libsndfile_example)
add_executable(${target} main.cpp)
target_link_libraries(${target} PRIVATE sndfile)

With the fix of this CMake-issue, which will go into CMake 3.17, you could point variable CMAKE_PROJECT_sndfile_INCLUDE_BEFORE to a file which sets the CMake-policy CMP0063 appropriately and which will automatically be included before the call to project(sndfile). As a result you won't get this warning for your fetched project.

This is a misunderstanding of the CMake documentation. The CMAKE_ARGS is part of the Configure Step options not download or update/patch options of the ExternalProject_Add() and is ignored.
Looking at the documentation for CMake (3.12) [https://cmake.org/cmake/help/v3.12/module/FetchContent.html]
The contentOptions can be any of the download or update/patch
options that the ExternalProject_Add() command understands. The
configure, build, install and test steps are explicitly disabled and
therefore options related to them will be ignored.
To avoid the messages you see you need to invoke cmake as cmake -Wno-dev on the command line when building your project.

Related

How to add dependencies between ExternalProjects in CMake?

I want to build a library libpng from source which requires libz which in turn I also want to build from source. I have one CMakeLists.txt file so far and the part that defines the build steps for both libraries looks like this:
if(NOT EXISTS ${CMAKE_BINARY_DIR}/build-zlib/build/libz.a)
file(DOWNLOAD https://www.zlib.net/zlib-1.2.11.tar.gz zlib.tar.gz TLS_VERIFY ON)
file(ARCHIVE_EXTRACT INPUT zlib.tar.gz)
include(ExternalProject)
ExternalProject_Add(zlib
SOURCE_DIR ${CMAKE_BINARY_DIR}/zlib-1.2.11
BINARY_DIR ${CMAKE_BINARY_DIR}/build-zlib/build
INSTALL_COMMAND cmake -E echo "Skipping install"
CMAKE_ARGS -DCMAKE_POSITION_INDEPENDENT_CODE=ON
)
message(STATUS "zlib will be built during 'make'")
endif()
if(NOT EXISTS ${CMAKE_BINARY_DIR}/build-png/build/libpng.a)
include(ExternalProject)
ExternalProject_Add(png
GIT_REPOSITORY "https://github.com/glennrp/libpng.git"
GIT_TAG "v1.6.37"
BINARY_DIR ${CMAKE_BINARY_DIR}/build-png/build
INSTALL_COMMAND cmake -E echo "Skipping install"
CMAKE_ARGS -DCMAKE_POSITION_INDEPENDENT_CODE=ON
)
message(STATUS "libpng will be built during 'make'")
endif()
There are two problems: First, CMake doesn't know that libz has to be built before libpng. Second, even if libz would be built before, CMake doesn't know that libz will be placed in ${CMAKE_BINARY_DIR}/build-zlib/build. I suppose the second problem may be fixed by adding list(APPEND CMAKE_PREFIX_PATH ${CMAKE_BINARY_DIR}/build-zlib/build) (please correct me if I'm wrong) but how do I deal with the first problem? Freely I've tried to add add_dependencies(png zlib) but libz is not getting built before libpng. The error message therefore states that libz can't be found:
CMake Error at /snap/cmake/876/share/cmake-3.20/Modules/FindPackageHandleStandardArgs.cmake:230 (message):
Could NOT find ZLIB (missing: ZLIB_LIBRARY ZLIB_INCLUDE_DIR)

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

BUILD_BYPRODUCTS and dependency cycle for cmake + ninja + googletest + ExternalProject_Add

Attempting to use external project to build google test like so.
# Add googletest
ExternalProject_Add( googletest
GIT_REPOSITORY https://github.com/google/googletest.git
# We don't need to run update command. Takes time
# and the version we initially d/l will shoudl be fine
CMAKE_ARGS = "-Dgtest_disable_pthreads=1"
# Don't run update
UPDATE_COMMAND ""
# Disable install step
INSTALL_COMMAND ""
# BUILD_BYPRODUCTS googletest-prefix/src/googletest-stamp/googletest-gitinfo.txt
# BUILD_BYPRODUCTS googletest-prefix/tmp/googletest-cfgcmd.txt
BUILD_BYPRODUCTS "googletest-prefix/src/googletest-build/googlemock/libgmock_main.a"
)
# Get include dirs for googletest framework
ExternalProject_Get_Property(googletest source_dir)
set(GTEST_INCLUDE_DIRS
${source_dir}/googlemock/include
${source_dir}/googletest/include
)
# Create library target for gmock main, which is used to create
# test executables
ExternalProject_Get_Property(googletest binary_dir)
set(GTEST_LIBRARY_PATH ${binary_dir}/googlemock/libgmock_main.a)
set(GTEST_LIBRARY gmock_main)
add_library(${GTEST_LIBRARY} UNKNOWN IMPORTED)
set_property(TARGET ${GTEST_LIBRARY} PROPERTY IMPORTED_LOCATION ${GTEST_LIBRARY_PATH})
add_dependencies(${GTEST_LIBRARY} googletest)
With the ninja generator I get the below warning.
Policy CMP0058 is not set: Ninja requires custom command byproducts to be
explicit. Run "cmake --help-policy CMP0058" for policy details. Use the
cmake_policy command to set the policy and suppress this warning.
This project specifies custom command DEPENDS on files in the build tree
that are not specified as the OUTPUT or BYPRODUCTS of any
add_custom_command or add_custom_target:
googletest-prefix/src/googletest-stamp/googletest-gitinfo.txt
googletest-prefix/tmp/googletest-cfgcmd.txt
For compatibility with versions of CMake that did not have the BYPRODUCTS
option, CMake is generating phony rules for such files to convince 'ninja'
to build.
Project authors should add the missing BYPRODUCTS or OUTPUT options to the
custom commands that produce these files.
If I oblige the request of the cmake error by uncommenting the build byproducts lines in my external project command, I get a cyclical dependency error. However, if I leave the build byproducts out of it, the project seems to build just fine.
$ ninja
ninja: error: dependency cycle: googletest-prefix/src/googletest-stamp/googletest-configure -> googletest-prefix/tmp/googletest-cfgcmd.txt -> googletest-prefix/src/googletest-stamp/googletest-configure
I'm using cmake 3.4, ninja 1.6, and running on Windows using MSYS2 package.
I added cmake_policy(SET CMP0058 NEW) to my toplevel CMakeLists.txt file as the --help-policy text explains to. It no longer generates the warnings afterwards. I guess those files aren't needed. Not sure how they're getting picked up as dependencies.
Try to use something like in ExternalProject_Add function:
set(GMOCK_FILE_DIR "gmock-${GMOCK_VERSION}/src/googletest_github-build/googlemock/")
BUILD_BYPRODUCTS "${GMOCK_FILE_DIR}gtest/libgtest_main.a"
BUILD_BYPRODUCTS "${GMOCK_FILE_DIR}gtest/libgtest.a"
BUILD_BYPRODUCTS "${GMOCK_FILE_DIR}libgmock_main.a"
BUILD_BYPRODUCTS "${GMOCK_FILE_DIR}libgmock.a"

Why set failed in CMAKE scripts

Suppose CMAKE_BINARY_DIR = C://a//b//c, and if I run the following cmake script:
cmake_minimum_required( VERSION 2.6 )
set(project_name "hello_cmake")
project(${project_name})
add_executable(hello src/main.cpp)
if(WIN32)
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE PATH "Installation Directory" )
else()
set(CMAKE_INSTALL_PREFIX "/usr/local" CACHE PATH "Installation Directory")
endif()
I expect CMAKE_INSTALL_PREFIX = C://a//b//c//install if I do not define CMAKE_INSTALL_PREFIX when I first call cmake .. from the folder C://a//b//c. However, it does not work in that way, and CMAKE_INSTALL_PREFIX is given a strange folder: C://Program Files (x86)//hello_cmake. Any ideas?
This is the recommended CMake incantation for handling CMAKE_INSTALL_PREFIX:
IF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
SET(CMAKE_INSTALL_PREFIX <path> CACHE PATH <comment> FORCE)
ENDIF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
Note the FORCE in the set. Apparently, the CMAKE_INSTALL_PREFIX is set fairly early in the invocation of CMake, which means it will already be set in the cache by the time you get to this line. Thus the need for FORCE.
Source is an email discussion on CMake mailing list: http://www.cmake.org/pipermail/cmake/2010-December/041135.html

ExternalProject_Add doesn't install the Release configuration of the project

EDIT: I have found a probable cause but I do not understand why: the last line in the below script Project(Externals) when removed fixes my issue. So the question now why??
cmake_minimum_required(VERSION 2.8)
include(ExternalProject)
MACRO(EXTERNAL_DEF aNewTargetName aPathToSource)
ExternalProject_Add(
${aNewTargetName}
PREFIX ${CMAKE_INSTALL_PREFIX}
SOURCE_DIR ${aPathToSource}
TMP_DIR "${CMAKE_INSTALL_PREFIX}/tmp/${CMAKE_BUILD_TYPE}"
DOWNLOAD_DIR "${CMAKE_INSTALL_PREFIX}/src/${CMAKE_BUILD_TYPE}"
BINARY_DIR "${CMAKE_INSTALL_PREFIX}/src/${CMAKE_BUILD_TYPE}/${aNewTargetName}-build"
STAMP_DIR "${CMAKE_INSTALL_PREFIX}/src/${CMAKE_BUILD_TYPE}/${aNewTargetName}-stamp"
CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
--debug-output
BUILD_COMMAND "${CMAKE_COMMAND}" --build
"${CMAKE_INSTALL_PREFIX}/src/${CMAKE_BUILD_TYPE}/${aNewTargetName}-build" --config "${CMAKE_BUILD_TYPE}"
#INSTALL_DIR "${CMAKE_INSTALL_PREFIX}"
)
ENDMACRO()
get_filename_component(zlibAbsPath "./zlib" ABSOLUTE)
EXTERNAL_DEF(zlib_external ${zlibAbsPath})
Project(Externals)
I invoke cmake on the above CMakeLists.txt file with CMAKE_INSTALL_PREFIX set to let's say "d:\externals" and CMAKE_BUILD_TYPE set to "Release"
Expectation:
I would expect only the Release configuration to get built. And after it gets built I would expect it to get installed in D:\externals\bin\zlib.dll.
Problem:
In reality, ExternalProject_Add builds both a Debug and a Release, and installs the debug version of the dll in D:\externals\bin\zlibd.dll
Isn't my build script correct? What am I doing wrong?
EDIT:
some more info. I just noticed. In the generated D:\externals\src\Release\zlib_external-build\zlib.sln, the INSTALL target is not selected to build at all. If I check it to build for the Release configuration then hit "Build" from visual studio, the INSTALL target builds and installs the files where I expect them. I have no idea what's going on...
CMAKE_BUILD_TYPE only work with single-configuration projects
you can change to:
BUILD_COMMAND ""
INSTALL_COMMAND
${CMAKE_COMMAND}
--build .
--target install
--config Release
BUILD_ALWAYS 1
Use DCMAKE_CFG_INTDIR variable.
...
CMAKE_ARGS
...
-DCMAKE_CFG_INTDIR=Release