Find directory where target is defined - cmake

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)

Related

CMake - How to get include directories of external project?

I tried to used https://github.com/julianxhokaxhiu/SteamworksSDKCI to use steam api on a simple SFML application (helloworld).
I wanted to use cmake to learn it, but I am struggling to understand how the provided CMakeLists and Find*.cmake file are expected to be used.
Currently, I have modified the CMakeLists to change the INSTALL_DIR
INSTALL_DIR "${CMAKE_BINARY_DIR}/../../vendor"
and my CMakeLists is :
cmake_minimum_required(VERSION 3.19)
project(SfmlWithCMake VERSION 1.0)
include(FetchContent)
set (CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_SOURCE_DIR}/cmake_steam")
# Configure external project
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/cmake_steam)
execute_process(
COMMAND ${CMAKE_COMMAND} ${CMAKE_SOURCE_DIR}/cmake_steam
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/cmake_steam
)
# Build external project
execute_process(
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}/cmake_steam
)
set(BUILD_SHARED_LIBS OFF)
FetchContent_Declare(
SFML
GIT_REPOSITORY https://github.com/SFML/SFML.git
GIT_TAG 2.5.1
)
FetchContent_MakeAvailable(SFML)
find_package(STEAMWORKSSDK REQUIRED)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED true)
# Generate config.h
configure_file(config.h.in config.h)
add_executable(
SfmlWithCMake
main.cpp
)
get_target_property(STEAMSDK STEAMWORKSSDK::STEAMWORKSSDK INCLUDE_DIRECTORIES)
target_include_directories(
SfmlWithCMake
PRIVATE
"${PROJECT_BINARY_DIR}"
"${STEAMSDK}"
)
target_link_libraries(
SfmlWithCMake
sfml-graphics
STEAMWORKSSDK::STEAMWORKSSDK
-static gcc stdc++ winpthread -dynamic
)
install(TARGETS SfmlWithCMake DESTINATION bin)
How to get include directories?
I do not succeed to add the steam include to the target_include_directories.
Here the ${STEAMSDK} is my last attempt to get the directory.
If I replace this by ${PROJECT_BINARY_DIR}/vendor/include, everything works.
Also, why does the SFML include are automatically added to my target include directories and not the steam one?
Am I using the Find*.cmake file the right way?
I understood that ExternalProject_Add was performed at build time and thus, as the find_package is needed at configue time, I added the two "execute_process". But, the readme on github only says to do the find package and add the target to target_link_libraries...
Thanks.

cmake linker error with library installed in custom path

I have compiled and installed with CMake the library SDL_bgi to a custom prefix /custom/prefix/. This library uses SDL2.
Now I want to use it in another project with the structure below but I get a linker error when I compile with make:
/usr/bin/c++ CMakeFiles/test.dir/test.cpp.o -o test -Wl,-rpath,/custom/prefix/lib: /custom/prefix/lib/libSDL_bgi.so
/usr/bin/ld: /custom/prefix/lib/libSDL_bgi.so: undefined reference to `SDL_DestroyWindow'
/usr/bin/ld: /custom/prefix/lib/libSDL_bgi.so: undefined reference to `SDL_CreateRenderer'
I have also written the file cmake/modules/FindSDL_bgi.cmake so that may be wrong as well.
If I compile with the following command I can compile correctly:
g++ test.cpp -I . -lSDL_bgi -lSDL2 -I /custom/prefix/include/ -L /custom/prefix/lib/
What am I doing wrongly?
Project structure:
cmake/modules/FindSDL_bgi.cmake
src/test/CMakeLists.txt
src/test/test.cpp
CMakeLists.txt
Libraries:
/usr/lib/libSDL.so
/usr/include/SDL.h
/custom/prefix/lib/libSDL_bgi.so
/custom/prefix/include/graphics.h
/custom/prefix/include/SDL2/libSDL_bgi.h
cmake/modules/FindSDL_bgi.cmake:
# - Try to find LibXml2
# Once done this will define
# SDL_BGI_FOUND - System has LibXml2
# SDL_BGI_INCLUDE_DIRS - The LibXml2 include directories
# SDL_BGI_LIBRARIES - The libraries needed to use LibXml2
# Hardcoded for now
set(SDL_BGI_PATH
/custom/prefix/
)
set(SDL_BGI_SEARCH_PATHS
/usr
/usr/local
/opt
${SDL_BGI_PATH}
)
find_path(SDL_BGI_INCLUDE_DIR graphics.h
HINTS
$ENV{SDL2DIR}
PATH_SUFFIXES include
PATHS ${SDL2_SEARCH_PATHS}
)
find_library(SDL_BGI_LIBRARY
NAMES SDL_bgi
HINTS
$ENV{SDL2DIR}
PATH_SUFFIXES lib64 lib
PATHS ${SDL2_SEARCH_PATHS}
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SDL_bgi REQUIRED_VARS SDL_BGI_LIBRARY SDL_BGI_INCLUDE_DIR)
CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(programmi_kennedy)
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules")
set(COMPAT_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/include/
)
find_package(SDL_bgi REQUIRED)
add_subdirectory(src/test)
src/CMakeLists.txt:
add_executable(test test.cpp)
target_include_directories(test PUBLIC ${SDL_BGI_INCLUDE_DIR})
target_link_libraries(test PRIVATE ${SDL_BGI_LIBRARY})
install(TARGETS test DESTINATION bin)
/custom/prefix/include/graphics.h:
#include <SDL2/SDL_bgi.h>
What I was missing is to link to SDL2 with find_package(SDL2 REQUIRED) and link to SDL2::SDL2. (I did try to link to ${SDL2_LIBRARIES} but the syntax is different now). Thanks to #KamilCuk to point me to the right direction.
EDIT:
I changed the FindBGI_sdl.cmake module in order to search for the dependencies (SDL2) and link against them using the INTERFACE keyword. In this way the target test can link only against SDL_bgi and have the dependencies resolved automatically.
src/CMakeLists.txt:
add_executable(test test.cpp)
target_link_libraries(test PRIVATE SDL_bgi::SDL_bgi)
install(TARGETS test DESTINATION bin)
cmake/modules/FindSDL_bgi.cmake:
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
#[=======================================================================[.rst:
FindSDL_bgi
-------
Finds the SDL_bgi library.
Imported Targets
^^^^^^^^^^^^^^^^
This module provides the following imported targets, if found:
``SDL_bgi::SDL_bgi``
The SDL_bgi library
Result Variables
^^^^^^^^^^^^^^^^
This will define the following variables:
``SDL_bgi_FOUND``
True if the system has the SDL_bgi library.
``SDL_bgi_VERSION``
The version of the SDL_bgi library which was found.
``SDL_bgi_INCLUDE_DIRS``
Include directories needed to use SDL_bgi.
``SDL_bgi_LIBRARIES``
Libraries needed to link to SDL_bgi.
Cache Variables
^^^^^^^^^^^^^^^
The following cache variables may also be set:
``SDL_bgi_INCLUDE_DIR``
The directory containing ``foo.h``.
``SDL_bgi_LIBRARY``
The path to the SDL_bgi library.
#]=======================================================================]
find_package(SDL2 REQUIRED)
find_package(PkgConfig)
pkg_check_modules(PC_SDL_bgi QUIET SDL_bgi)
find_path(SDL_bgi_INCLUDE_DIR
NAMES graphics.h
PATHS ${PC_SDL_bgi_INCLUDE_DIRS}
)
find_library(SDL_bgi_LIBRARY
NAMES SDL_bgi
PATHS ${PC_SDL_bgi_LIBRARY_DIRS}
)
set(SDL_bgi_VERSION ${PC_SDL_bgi_VERSION})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SDL_bgi
FOUND_VAR SDL_bgi_FOUND
REQUIRED_VARS
SDL_bgi_LIBRARY
SDL_bgi_INCLUDE_DIR
VERSION_VAR SDL_bgi_VERSION
)
if(SDL_bgi_FOUND AND NOT TARGET SDL_bgi::SDL_bgi)
add_library(SDL_bgi::SDL_bgi UNKNOWN IMPORTED)
set_target_properties(SDL_bgi::SDL_bgi PROPERTIES
IMPORTED_LOCATION "${SDL_bgi_LIBRARY}"
INTERFACE_COMPILE_OPTIONS "${PC_SDL_bgi_CFLAGS_OTHER}"
INTERFACE_INCLUDE_DIRECTORIES "${SDL_bgi_INCLUDE_DIR}"
)
target_link_libraries(SDL_bgi::SDL_bgi INTERFACE SDL2::SDL2)
endif()
mark_as_advanced(
SDL_bgi_INCLUDE_DIR
SDL_bgi_LIBRARY
SDL2_DIR
)
Useful references:
https://pabloariasal.github.io/2018/02/19/its-time-to-do-cmake-right/
https://cmake.org/cmake/help/v3.17/manual/cmake-developer.7.html

How to add_subdirectory after a externalproject_add()?

I have to download zlib to use in my project.
I use externlproject_add() to downaload all zlib repository, build and install it.
After it, I want do install a lib that is part of zlib repository: minizip.
How to set this dependency on cmake?
zlib module:
cmake_minimum_required ( VERSION 2.8.7 )
include (ExternalProject)
if(UNIX)
# An external project for zlib
SET (GIT_URL https://github.com/madler/zlib.git)
SET (ZLIB_INSTALL ${CMAKE_CURRENT_BINARY_DIR})
SET (ZLIB_INCLUDE ${CMAKE_BINARY_DIR}/include/zlib)
SET (ZLIB_STATIC ${CMAKE_BINARY_DIR}/lib/libz.a )
SET (MINIZIP_DIR ${CMAKE_CURRENT_BINARY_DIR}/ZLIB/src/ZLIB/contrib/minizip)
ExternalProject_Add(zlib
PREFIX zlib
GIT_REPOSITORY ${GIT_URL}
INSTALL_DIR ${ZLIB_INSTALL}
PATCH_COMMAND ${CMAKE_COMMAND} -E remove <SOURCE_DIR>/zconf.h
BUILD_IN_SOURCE 1
PATCH_COMMAND ""
CONFIGURE_COMMAND <SOURCE_DIR>/configure --prefix=<INSTALL_DIR> --includedir=${ZLIB_INCLUDE}
)
find_package(ZLIB REQUIRED)
if(ZLIB_FOUND)
add_subdirectory (${MINIZIP_DIR})
endif(ZLIB_FOUND)
SET (ZLIB_INCLUDE_DIR ${ZLIB_INSTALL}/include/zlib)
SET (ZLIB_LIBRARY "${ZLIB_INSTALL}")
ADD_LIBRARY (ZLIB_LIB STATIC IMPORTED DEPENDS zlib)
SET_TARGET_PROPERTIES (ZLIB_LIB PROPERTIES IMPORTED_LOCATION "${ZLIB_STATIC}")
endif(UNIX)
With this zlib module I have an error that following directory
/home/lais/Imagens/agent/build/ZLIB/src/ZLIB/contrib/minizip
doesn't exist yet when I run
cmake ..
And it's true. Doesn't exist yet. I should tell cmake it. But I don't know how to do it.
I solved it download a zip file that have minizip.
cmake_minimum_required ( VERSION 2.8.7 )
include (ExternalProject)
project (zlib)
if(UNIX)
# An external project for zlib
SET (URL http://www.winimage.com/zLibDll/unzip101e.zip)
SET (MINIZIP_INSTALL ${CMAKE_CURRENT_BINARY_DIR})
SET (MINIZIP_STATIC ${CMAKE_BINARY_DIR}/lib/minizip.a )
message ("zlib = ${zlib}")
ExternalProject_Add(minizip
DEPENDS zlib
PREFIX minizip
URL ${URL}
INSTALL_DIR ${MINIZIP_INSTALL}
BUILD_IN_SOURCE make
CONFIGURE_COMMAND ""
)
SET (MINIZIP_LIBRARY "${MINIZIP_INSTALL}")
ADD_LIBRARY (MINIZIP_LIB STATIC IMPORTED DEPENDS minizip, zlib)
SET_TARGET_PROPERTIES (MINIZIP_LIB PROPERTIES IMPORTED_LOCATION "${MINIZIP_STATIC}")
endif(UNIX)

cmake external projects command seems to ignore INSTALL_DIR

First off, I'm relatively new to cmake. I'm trying to use cmake to build a project with a single external dependency. I specify the INSTALL_DIR for the external project to be CMAKE_INSTALL_PREFIX, so it installs to the same place as the parent project. But when I run make, it ignores it and tries to install to /usr/local/lib.
Here's my CMakeList.txt:
cmake_minimum_required( VERSION 2.8 )
include( ExternalProject )
project( capture )
add_library( capture SHARED capture.cc )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11" )
ExternalProject_Add( proj_exceptions
GIT_REPOSITORY /home/user/workspace/exceptions
INSTALL_DIR ${CMAKE_INSTALL_PREFIX}
)
add_library( exceptions SHARED IMPORTED )
set_property( TARGET exceptions
PROPERTY IMPORTED_LOCATION ${CMAKE_INSTALL_PREFIX}/lib/libexceptions.so
)
add_dependencies( exceptions proj_exceptions )
include_directories( ${CMAKE_INSTALL_PREFIX}/include )
target_link_libraries( capture exceptions )
install( TARGETS capture DESTINATION lib )
install( FILES capture.h DESTINATION include )
CMakeLists.txt for the external project looks like this:
cmake_minimum_required( VERSION 2.8 )
project( exceptions )
add_library( exceptions SHARED exceptions.cc )
install( TARGETS exceptions DESTINATION lib )
install( FILES exceptions.hh DESTINATION include )
It clones and builds the external project just fine, but it chokes on the install step:
Install the project...
-- Install configuration: ""
-- Installing: /usr/local/lib/libexceptions.so
CMake Error at cmake_install.cmake:42 (file):
file INSTALL cannot copy file
"/home/user/workspace/capture/build/proj_exceptions-prefix/src/proj_exceptions-build/libexceptions.so"
to "/usr/local/lib/libexceptions.so".
Makefile:66: recipe for target 'install' failed
As you can see, the install configuration is empty. Looking through the generated config for the external project, I found this in cmake_install.cmake:
if(NOT DEFINED CMAKE_INSTALL_PREFIX)
set(CMAKE_INSTALL_PREFIX "/usr/local")
endif()
So, it seems that passing INSTALL_DIR to ExternalProject_Add doesn't set the install prefix. The install step succeeds, if I instead use:
ExternalProject_Add( proj_exceptions
GIT_REPOSITORY /home/djones/workspace/exceptions
CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}"
)
So what's the purpose of INSTALL_DIR then?
You're right for the purpose of INSTALL_DIR, but you may have missed some steps.
According the cmake 2.8 doc about external project:
Install Step
The INSTALL_DIR is underneath the calling project’s
binary directory. Use INSTALL_DIR to specify a different location.
Note that in addition to setting INSTALL_DIR, you also have to pass
-DCMAKE_INSTALL_PREFIX or --prefix to the CMake or configure command. It is not used automatically in the configure step since not all
projects follow this convention.
# [INSTALL_DIR dir]
You can refer to the install directory in your configure command, for
example:
CONFIGURE_COMMAND SOURCE_DIR/configure --prefix=INSTALL_DIR
# [INSTALL_COMMAND cmd...]
CMake-based projects use ‘cmake--build’ to build the install target.
Other projects use ‘make install’. Use INSTALL_COMMAND to customize
the install step. Use INSTALL_COMMAND “” to omit the install step. The
install command executes with the working directory set to
.
So try to update your cmake command, or use the custom INSTALL_COMMAND feature.

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