Why set failed in CMAKE scripts - cmake

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

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.

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

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.

CMake Error: file INSTALL destination is not a directory

I am running the command "sudo make install", the relevant cmake_install.cmake file is at the bottom. The exact error message I receive is:
CMake Error at cmake_install.cmake:36 (file):
file INSTALL destination:
~/Desktop/Geant/geant4.10.04-install/share/Geant4-10.4.0/geant4make is not
a directory.
Makefile:104: recipe for target 'install' failed
make: *** [install] Error 1
This is perplexing to me as I can navigate to that exact directory, it exists and whats more, it was made during this installation, so the make install is creating this directory and then saying that it doesn't exist...
Also, when I originally did the cmake command, my CMAKE_INSTALL_PREFIX is "~/Desktop/Geant/geant4.10.04-install", but since the make install command was able to make the geant4.10.04-install directory in the correct place, I don't think that is the problem.
The first 50ish lines of the cmake_install.cmake file (I can post the rest if need be...) :
# Install script for directory: /home/kagnew/Desktop/Geant/geant4.10.04
# Set the install prefix
if(NOT DEFINED CMAKE_INSTALL_PREFIX)
set(CMAKE_INSTALL_PREFIX "~/Desktop/Geant/geant4.10.04-install")
endif()
string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
# Set the install configuration name.
if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)
if(BUILD_TYPE)
string(REGEX REPLACE "^[^A-Za-z0-9_]+" ""
CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}")
else()
set(CMAKE_INSTALL_CONFIG_NAME "Release")
endif()
message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"")
endif()
# Set the component getting installed.
if(NOT CMAKE_INSTALL_COMPONENT)
if(COMPONENT)
message(STATUS "Install component: \"${COMPONENT}\"")
set(CMAKE_INSTALL_COMPONENT "${COMPONENT}")
else()
set(CMAKE_INSTALL_COMPONENT)
endif()
endif()
# Install shared libraries without execute permission?
if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE)
set(CMAKE_INSTALL_SO_NO_EXE "1")
endif()
if(NOT CMAKE_INSTALL_COMPONENT OR "${CMAKE_INSTALL_COMPONENT}" STREQUAL "Development")
file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/share/Geant4-10.4.0/geant4make" TYPE FILE MESSAGE_LAZY PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE FILES "/home/kagnew/Desktop/Geant/geant4-build/InstallTreeFiles/geant4make.sh")
endif()
UPDATE: As suggested by Tsyvarev, changing the beginning of my prefix path from "~" to "/home/user/" seems to have fixed the problem
Using the environmental variable $ENV{HOME} is preferable to hardcoding /home/<user> because it will use the correct top-level directory (i.e. /Users instead of /home on macOS, if you're doing a cross-platform build), and it will automatically expand to include the name of the user invoking cmake, making it better suited to collaborative environments.
Additionally, using $ENV{HOME} should make the sudo in front of make install unnecessary, though depending on when the variable is expanded, $ENV{HOME} may refer to the user invoking cmake or the user invoking make install (i.e. /root if you use sudo), so your mileage may vary.
EDIT: I found my way to this question because I was getting the same "CMake Error: file INSTALL destination is not a directory" output due to using ~. It would seem that for certain purposes CMake just really doesn't like ~. $ENV{HOME} has exactly the same value as ~, except that CMake doesn't freak out when you try to use it in CMAKE_INSTALL_PREFIX.

Find directory where target is defined

Is there a way in cmake to find the source directory of the CMakeLists.txt file in which a target was defined?
Somethign like:
if (TARGET Foo)
message("Library Foo was alread built in ${LOCATION_OF_FOOS_CMAKE}")
else()
add_library(Foo ...)
endif()
Edit:
Unfortunately, my cmake scripts have to work on a default ubuntu 14.04 installation. So I'm limited to cmake 2.8
You can use target property SOURCE_DIR (added with CMake 3.7):
get_target_property(FOO_SOURCE_DIR Foo SOURCE_DIR)
For older versions of CMake you can overwrite e.g. add_library() and define your own SOURCE_DIR target property:
function(add_library _target)
_add_library(${_target} ${ARGN})
set_target_properties(${_target} PROPERTIES SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
endfunction(add_library)

Building GLFW3 Application with CMAKE - GLFW_LIBRARIES doesnt set

I'm attempting to build a small project using glfw3 but no matter what I do I can't get pkgconfig to set GLFW_LIBRARIES.
Here is my CMakeList.txt
cmake_minimum_required(VERSION 3.3)
project(LearnGLSL)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
if (CMAKE_BUILD_TYPE STREQUAL "")
set(CMAKE_BUILD_TYPE Debug)
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/build/debug")
set(PROJECT_BINARY_DIR "${CMAKE_SOURCE_DIR}/build/debug")
endif(CMAKE_BUILD_TYPE STREQUAL "Debug")
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
find_package(OpenGL REQUIRED)
find_package(PkgConfig REQUIRED)
pkg_check_modules(GLFW REQUIRED glfw3)
include_directories(
${OPENGL_INCLUDE_DIR}
${GLFW_INCLUDE_DIRS}
)
set(SOURCE_FILES main.cpp gl_core_4_3.cpp)
message(WARNING "${GLFW_LIBRARIES}")
add_executable(LearnGLSL ${SOURCE_FILES})
target_link_libraries(LearnGLSL ${OPENGL_gl_LIBRARY} ${GLFW_LIBRARIES})
add_custom_command(TARGET LearnGLSL POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/assets
${PROJECT_BINARY_DIR}
COMMENT "Copy resources to build tree")
Here is where glfw3 is installed
-- Installing: /usr/local/include/GLFW
-- Installing: /usr/local/include/GLFW/glfw3native.h
-- Installing: /usr/local/include/GLFW/glfw3.h
-- Installing: /usr/local/lib/cmake/glfw/glfw3Config.cmake
-- Installing: /usr/local/lib/cmake/glfw/glfw3ConfigVersion.cmake
-- Installing: /usr/local/lib/cmake/glfw/glfwTargets.cmake
-- Installing: /usr/local/lib/cmake/glfw/glfwTargets-noconfig.cmake
-- Installing: /usr/local/lib/pkgconfig/glfw3.pc
-- Installing: /usr/local/lib/libglfw3.a
I'll be the first to admit I'm not super comfortable with CMAKE but this seems simple enough and I've done everything I can google to find. maybe its a typo i'm not noticing. Any help is appreciated thanks
Oh i forgot to mention I get undefined references to the glfw functions when building this project. I assumed this is a result of GLFW_LIBRARIES not properly getting set tho.
I don't know about finding GLFW with pkgconfig but I don't think you need pkgconfig in this case. Since GLFW itself builds with CMake it should install a native CMake config module, which it does.
Well, almost. The official GLFW CMake config-module support is a bit buggy as of v3.1.2. Instead, use shaxbee's fork or the adasworks fork (based on shaxbee's but newer)
With that GLFW all you need to find it is just 2 lines:
find_package(glfw3 REQUIRED)
...
target_link_libraries(LearnGLSL ... glfw)
I also found a few other problems in your CMakeLists.txt so I repeat the whole script, revised:
cmake_minimum_required(VERSION 3.3)
project(LearnGLSL)
set(CMAKE_CXX_STANDARD 11) # no explicit compiler flags if possible
# don't read CMAKE_BUILD_TYPE, it has no meaning with multiconfig
# generators
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_SOURCE_DIR}/build/debug")
# PROJECT_BINARY_DIR should not be set at all
# You establish the BINARY_DIR with the initial cmake command
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
find_package(OpenGL REQUIRED)
find_package(glfw3 REQUIRED)
include_directories(${OPENGL_INCLUDE_DIR})
add_executable(LearnGLSL main.cpp gl_core_4_3.cpp)
target_link_libraries(LearnGLSL ${OPENGL_gl_LIBRARY} glfw)
add_custom_command(TARGET LearnGLSL POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/assets
${PROJECT_BINARY_DIR}
COMMENT "Copy resources to build tree")
Since CMake 3.1 pkg_check_modules uses additional paths from CMAKE_PREFIX_PATH variable for search .pc files. Searching is performed in similar manner as in command find_library, but additional subdirectory pkgconfig/ is added to the resulted path. Specifically, for each <prefix> in CMAKE_PREFIX_PATH, .pc file is searched in next directory:
<prefix>/lib[64]/[<arch>/]pkgconfig
(suffix 64 and arhitecture-specific subdirectory is added when appropriate).
So having file /usr/local/lib/pkgconfig/glfw3.pc, you need to set CMAKE_PREFIX_PATH to /usr/local for pkg_check_modules is able to find it. Variable can be set either:
1) In the CMakeLists.txt script itself, or
2) In the command line
cmake -DCMAKE_PREFIX_PATH=<...> <source-dir>
3) As environment one (OS-dependent).
Before CMake 3.1 (and after it) additional search directory can be specified via PKG_CONFIG_PATH environment variable.
E.g. with file /usr/local/lib/pkgconfig/glfw3.pc variable PKG_CONFIG_PATH should contain /usr/local/lib/pkgconfig.