Manually do the work of find_package by specifying the paths ons the command line - cmake

A colleague has a project which uses a hand-written Makefile with hard-coded library paths. So for instance the CXXFLAGS and the LDFLAGS are set like the following:
-I/home/personA/libraries/eigen-3.2.7
-I/home/personA/libraries/boost_1_60_0
-I/home/personB/hdf5-1.8.17/include
-L/home/personA/libraries/boost_1_60_0/stage/lib/
-L/home/personB/hdf5-1.8.17/lib
Nobody has direct administrative rights on this machine, so just installing the Debian packages with those libraries will involve nagging the administrator to install them. And even if he does, there might be a different dependency that is not in the repositories.
In my CMake file, I have this:
find_package(HDF5 REQUIRED COMPONENTS C CXX)
include_directories(${HDF5_INCLUDE_DIRS})
find_package(Boost REQUIRED COMPONENTS filesystem system program_options)
find_package(Eigen3 REQUIRED)
include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR})
find_package(OpenMP)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
On my Fedora workstation, this works just fine. On my Ubuntu 14.04 virtual machine, it also works, it also builds on Travis CI. However, this project runs on our compute cluster and the dependencies are in really odd places.
So I would like to invoke cmake in a way that tells it that I already know that include and library flags it needs and not even bother to look for a FindEigen3.cmake file (which is not there).
Is there some way to override the find_package and just specify the paths manually?

You can take advantage of the fact that find_package() does only look for the libraries/include paths until it has found the requested package and stores a successful finding fact in _FOUND variables.
So in your case - taken the Eigen3 example - you can do:
> cmake -D Eigen3_FOUND:BOOL=ON -D EIGEN3_INCLUDE_DIR:PATH=/home/personA/libraries/eigen-3.2.7 ..

Just let the user set the variables that find_package would set manually. Then skip the find_package altogether:
if(NOT DEFINED EIGEN3_INCLUDE_DIRS)
find_package(Eigen3 REQUIRED)
endif()
include_directories(SYSTEM ${EIGEN3_INCLUDE_DIRS})
This has the advantage that one does not even need an FindEigen3.cmake file.

Related

Can't seem to include Glut to my cmake project Ubuntu [duplicate]

I've been stuck for a while now and I can't figure out how to get freeglut working. I thought I knew what it was asking me to do, so I added that set(prefix_path) line but it didn't do anything. Am I supposed to write my own freeglut-config.cmake or what?
Note: I am using the freeglut for MinGW package from this website
CMake File:
cmake_minimum_required(VERSION 3.7)
project(HW1)
set(CMAKE_CXX_STANDARD 11)
set(SOURCE_FILES Triangle.cpp)
set(CMAKE_PREFIX_PATH "C:/freeglut")
find_package(GLEW REQUIRED STATIC)
find_package(FREEGLUT REQUIRED)
find_package(OPENGL REQUIRED)
include_directories(${FREEGLUT_INCLUDE_DIRS} ${GLEW_INCLUDE_DIRS} ${OPENGL_INCLUDE_DIRS})
link_directories(${FREEGLUT_LIBRARY_DIRS} ${GLEW_LIBRARY_DIRS} ${OPENGL_LIBRARY_DIRS})
add_definitions(${FREEGLUT_DEFINITIONS} ${GLEW_DEFINITIONS} ${OPENGL_DEFINITIONS})
add_executable(HW1 ${SOURCE_FILES})
target_link_libraries(HW1 ${FREEGLUT_LIBRARIES} ${GLEW_LIBRARIES} ${OPENGL_LIBRARIES})
Full error:
CMake Error at CMakeLists.txt:8 (find_package):
By not providing "FindFREEGLUT.cmake" in CMAKE_MODULE_PATH this project has
asked CMake to find a package configuration file provided by "FREEGLUT",
but CMake did not find one.
Could not find a package configuration file provided by "FREEGLUT" with any
of the following names:
FREEGLUTConfig.cmake
freeglut-config.cmake
Add the installation prefix of "FREEGLUT" to CMAKE_PREFIX_PATH or set
"FREEGLUT_DIR" to a directory containing one of the above files. If
"FREEGLUT" provides a separate development package or SDK, be sure it has
been installed.
If your application is GLUT-compatible, that it doesn't use any extension of freeglut, then it is better to search GLUT instead of FREEGLUT:
find_package(GLUT REQUIRED)
"Find" script used by this command is already shipped into CMake distro, and it searches freeglut too.
(Note, that with that command variables for include directories and linking libraries are GLUT_INCLUDE_DIR and GLUT_LIBRARY correspondingly).
If your application requires exactly freeglut (that is, uses some of its extensions incompatible with other GLUT implementations), you need to ship your package with FindFREEGLUT.cmake script and adjust CMAKE_MODULE_PATH variable correspondingly:
# Assuming you have <source-dir>/cmake/FindFREEGLUT.cmake
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
find_package(FREEGLUT REQUIRED)
You may find existing script in the net, or write it by yourself, like here.
In any case, if you have freeglut installed into non-system location, you need to hint CMake about that. E.g., by adjusting CMAKE_PREFIX_PATH.

Add FreeType dependency with cmake supporting all vcpkg/apt-get/brew

I have a project which depends on FreeType, and uses CMake as build system. CMake has a FindFreeType built-in module which is supposed to be used like this, see for example this other SO question:
find_package(Freetype REQUIRED)
target_link_libraries(mylib ${FREETYPE_LIBRARIES})
target_include_directories(mylib PRIVATE ${FREETYPE_INCLUDE_DIRS})
Since CMake 3.10, there is also the Freetype::Freetype imported target so we can avoid the target_include_directories:
find_package(Freetype REQUIRED)
target_link_libraries(mylib Freetype::Freetype)
This worked great on Ubuntu 18.04 with FreeType installed via apt install libfreetype6-dev. I assume it also works on macOS when the package is installed via homebrew (I haven't tested yet).
However, on Windows, I wish to allow developers to depend on a vcpkg-installed FreeType:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
.\bootstrap-vcpkg.bat
.\vcpkg integrate install
.\vcpkg install freetype:x64-windows
Which they would target by running the following CMake command:
cmake .. -G "Visual Studio 15 2017" -A x64 -DCMAKE_TOOLCHAIN_FILE=C:/Users/Boris/vcpkg/scripts/buildsystems/vcpkg.cmake
Unfortunately, the above CMake command won't work with any of the two CMakeLists.txt at the beginning of this question, because the proper way to find and link to FreeType when it is installed via vcpkg is the following:
find_package(freetype CONFIG REQUIRED) # `Freetype` works too, but vcpkg doc recommends `freetype`
target_link_libraries(mylib freetype) # Here, all-lowercase is required
In particular, the freetype-config.cmake config file provided by vcpkg defines the target freetype (not Freetype::Freetype like the builtin find module), and doesn't define any of the FREETYPE_LIBRARIES or FREETYPE_INCLUDE_DIRS variables.
What would be a proper way to keep my CMakeLists.txt compatible with both "traditional" ways of finding FreeType, but also vcpkg?
Assuming pre-CMake 3.10, I'm thinking of something along the lines of:
if(DEFINED VCPKG_TARGET_TRIPLET)
find_package(freetype CONFIG REQUIRED)
set(FREETYPE_LIBRARIES freetype)
set(FREETYPE_INCLUDE_DIRS "")
else()
find_package(Freetype REQUIRED)
endif()
target_link_libraries(mylib ${FREETYPE_LIBRARIES})
target_include_directories(mylib PRIVATE ${FREETYPE_INCLUDE_DIRS})
Would that seem like good practice? Any better idea?
It feels ugly, and besides, there is always the possibility of a developer wanting to use vcpkg for some other dependencies but not for FreeType (e.g., explicitly providing FREETYPE_DIR instead), so this trick wouldn't even be enough in all situations, and we would need another CMake option like MYLIB_IGNORE_VCPKG_FREETYPE which starts to be even uglier.
Would that seem like good practice? Any better idea?
No be agnostic about a possible package manager.
Do the following:
find_package(Freetype CONFIG) # should find freetype-config.cmake if available
find_package(Freetype REQUIRED) # Will not be executed if Freetype_FOUND ist already set
# if you do not want two find_package calls consider using CMAKE_FIND_PACKAGE_PREFER_CONFIG
Then test if the target Freetype::Freetype or freetype exists
if(TARGET freetype AND NOT TARGET Freetype::Freetype)
add_library(Freetype::Freetype ALIAS freetype) # target freetype is defined by freetype-targets.cmake
# might need to add freetype to global scope if cmake errors here
# alternativly if the above does not work for you you can use
# add_library(Freetype::Freetype INTERFACE IMPORTED)
# target_link_libraries(Freetype::Freetype INTERFACE freetype)
endif()
if(NOT TARGET Freetype::Freetype)
# insert error here
# or create the target correctly (see cmakes newer FindFreetype.cmake)
endif()
target_link_libraries(mylib PRIVATE Freetype::Freetype)
if you don't want to alias the target you could also define a variable called FREETYPE_TARGET and set it to the correct target for linking against.
In older versions of vcpkg (< Jan 2020, see vcpkg#9311), there used to be the following message when installing Freetype:
The package freetype is compatible with built-in CMake targets:
find_package(Freetype REQUIRED)
target_link_libraries(main PRIVATE Freetype::Freetype)
In current versions, they instead recommend find_package(freetype CONFIG REQUIRED), which ensures that the config package vcpkg/<...>/freetype-config.cmake takes precedence over the CMake's built-in module package FindFreetype.cmake.
However, using the built-in module package still works correctly: it will find the vcpkg-installed library, and will define a Freetype::Freetype target (if CMake >= 3.10) rather than a freetype target.
Note that by default, find_package(Freetype REQUIRED) searches first for module packages, then for config packages. However, users can sets CMAKE_FIND_PACKAGE_PREFER_CONFIG, which would use the config package instead. Therefore, a robust approach to ensure that Freetype::Freetype is defined is to do the following:
find_package(Freetype MODULE REQUIRED)
target_link_libraries(mylib Freetype::Freetype)
However, if CMake < 3.10, this still doesn't define a Freetype::Freetype target. One option in this case is to vendor a copy of a more recent version of FindFreetype.cmake in your CMAKE_MODULE_PATH.
For even more robustness, you could even call it Find<Mylib>Freetype.cmake, in the unlikely case where your CMakeLists.txt is used as a subdirectory of another project which also modifies CMAKE_MODULE_PATH and provides an incompatible FindFreetype.cmake.

How to find static version of zlib in CMake?

I'm on cmake version 3.12.1 and want to build a static executable that uses ZLIB. I have both the static (libz.a) and shared (libz.so) libraries on my machine. How can I tell find_package(ZLIB) to return the static version? Maybe there's another way to find libz.a as well?
My present workaround is to specify:
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
Then:
target_link_libraries (my_binary z lib1 lib2)
Critique on this approach is also welcome!
As of CMake 3.24, use: set(ZLIB_USE_STATIC_LIBS "ON")
Source
Your approach is valid given the limitations of the CMake module called by find_package(ZLIB), specifically FindZLIB.cmake. While other FindXXX.cmake modules have a special option for grabbing static libraries, the zlib module does not.
There are already a few questions on SO about this topic, but some are older than others, so there are a few options.
You can instead apply the -static flag on a more granular level (rather than editing the global CMAKE_EXE_LINKER_FLAGS variable) by adding it to your target_link_libraries call. This way it will apply only to that target -- useful if you are building other non-static targets.
You could also tell CMake to search for static libraries explicitly by setting CMAKE_FIND_LIBRARY_SUFFIXES. When find_package is called, CMake can search for libraries ending in .a using this:
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
find_package(ZLIB REQUIRED)
If you have control over installing zlib, for example, you are installing dependencies in a Continuous Integration setup, I would recommend to just remove the zlib dynamic library.
zlib doesn't have the option to build statically or dynamically, it automatically generates both versions. However FindZlib.cmake prioritizes the dynamic version.
I find the following approach to be better in case you don't have access to modify third parties repositories CMakeLists.txt that needs zlib:
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(_compiler_is_msvc ON)
endif()
option(ZLIB_FORCE_STATIC "Remove the dynamic libraries after zlib install" ON)
mark_as_advanced(ZLIB_FORCE_STATIC)
set(OUTPUT_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR} CACHE PATH "Base folder where builds and source folder will be installed: i.e. OUTPUT_BUILD_DIR/zlib")
if(_compiler_is_msvc)
set(ZLIB_GIT_TAG cacf7f1d4e3d44d871b605da3b647f07d718623f) # Version 1.2.11
message(STATUS "ZLIB_VERSION: ${ZLIB_GIT_TAG} : Version 1.2.11")
set(ZLIB_BUILD_DIR ${OUTPUT_BUILD_DIR}/zlib-build)
set(ZLIB_INSTALL_DIR ${OUTPUT_BUILD_DIR}/zlib)
set(ZLIB_SRC_FOLDER_NAME zlib-src)
set(ZLIB_SRC_DIR ${OUTPUT_BUILD_DIR}/${ZLIB_SRC_FOLDER_NAME})
set(ZLIB_GIT_REPOSITORY "https://github.com/madler/zlib")
ExternalProject_Add(ep_zlib
GIT_REPOSITORY ${ZLIB_GIT_REPOSITORY}
GIT_TAG ${ZLIB_GIT_TAG}
# GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
CMAKE_GENERATOR ${CMAKE_GENERATOR}
SOURCE_DIR ${ZLIB_SRC_DIR}
BINARY_DIR ${ZLIB_BUILD_DIR}
CMAKE_ARGS
-DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER}
-DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER}
-DCMAKE_BUILD_TYPE:STRING=${SGEXT_CMAKE_BUILD_TYPE}
-DBUILD_SHARED_LIBS:BOOL=OFF
-DCMAKE_INSTALL_PREFIX=${ZLIB_INSTALL_DIR}
)
if(ZLIB_FORCE_STATIC)
ExternalProject_Add_Step(
ep_zlib zlib_remove_dll
COMMENT "Remove zlib.lib and zlib.dll, leaves only zlibstatic.lib"
DEPENDEES install
COMMAND ${CMAKE_COMMAND} -E remove -f ${ZLIB_INSTALL_DIR}/lib/zlib.lib ${ZLIB_INSTALL_DIR}/bin/zlib.dll
)
endif()
endif()
The last step removes the dynamic version, so the default FindZLIB will find the static library.
The best solution I found was to name the library explicitly when calling CMake:
cmake -DZLIB_LIBRARY=/usr/lib/x86_64-linux-gnu/libz.a /path/to/source
I would not recommend the solution proposed by #phcerdan because in my case the installed shared library was colliding with an already installed version, so the only solution was to make sure it never gets installed in the first place. The key idea is to disable completely the targets installation using SKIP_INSTALL_LIBRARIES, and instead to "install" the static library manually. Nonetheless, my solution is quite similar:
EXTERNALPROJECT_ADD(zlib_external
GIT_REPOSITORY https://github.com/madler/zlib.git
GIT_TAG v1.2.11
CMAKE_ARGS
-DSKIP_INSTALL_FILES=ON # Disable install of manual and pkgconfig files
-DSKIP_INSTALL_LIBRARIES=ON # Do not install libraries automatically. It will be handled manually to avoid installing shared libs
-DBUILD_SHARED_LIBS=OFF
-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}
-DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX}
-DCMAKE_C_FLAGS:STRING=${CMAKE_COMPILE_FLAGS_EXTERNAL}
${EXTERNALPROJECT_BUILD_TYPE_CMD}
INSTALL_DIR ${CMAKE_INSTALL_PREFIX}
)
if(NOT WIN32)
set(zlib_BUILD_LIB_PATH "<BINARY_DIR>/libz.a")
set(zlib_PATH "${CMAKE_INSTALL_PREFIX}/lib/libz.a")
else()
set(zlib_BUILD_LIB_PATH "<BINARY_DIR>/Release/zlibstatic.lib")
set(zlib_PATH "${CMAKE_INSTALL_PREFIX}/lib/zlibstatic.lib")
endif()
ExternalProject_Add_Step(
zlib_external zlib_install_static_only
COMMENT "Manually installing only static library"
DEPENDEES install
COMMAND ${CMAKE_COMMAND} -E copy ${zlib_BUILD_LIB_PATH} ${zlib_PATH}
)

Clion, cMake and POCO

I'm a new guy to c++ and cmake here. I decided to test out cLion and cMake. I'm trying to write a simple email client for the command line. Other sources told me that the best way to implement a POP3 and SMTP functions would be to use POCO. Unfortunately, cMake is giving me trouble. The version that came with CLion is 3.2 but the version that my machine is running is 2.8.
~$ cmake --version
cmake version 2.8.12.2
First problem. I thought that I could bypass this by just installing POCO and doing the same thing that I used for openssl which I also had to download.
cMakeList.txt:
cmake_minimum_required(VERSION 3.0)
project(Email_Reader)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
#included paths for openssl and POCO.
INCLUDE_DIRECTORIES("/usr/include/openssl")
INCLUDE_DIRECTORIES("/usr/local/include/Poco/Net")
set(SOURCE_FILES main.cpp)
add_executable(Email_Reader ${SOURCE_FILES})
The documentation for POCO tells me that I need at least 3.0 to work but I feel I have 2 different cMakes on my machine. Can you help me, please?
You can get the latest CMake release from: http://www.cmake.org/download/
For Linux, it's this archive: http://www.cmake.org/files/v3.2/cmake-3.2.2.tar.gz
An easy way to use it is to put the extracted files in /opt/cmake/cmake-3.2 then create the following aliases (e.g. in ~/.bash_aliases:
alias ccmake3='/opt/cmake/cmake-3.2/bin/ccmake'
alias cmake3='/opt/cmake/cmake-3.2/bin/cmake'
alias cmake3-gui='/opt/cmake/cmake-3.2/bin/cmake-gui'
alias cpack3='/opt/cmake/cmake-3.2/bin/cpack'
alias ctest3='/opt/cmake/cmake-3.2/bin/ctest'
Then, make sure that you have properly built and installed POCO.
The Getting Started page has all the information you need for doing that. But, basically, you should get the sources from here and extract them somehwere:
wget http://pocoproject.org/releases/poco-1.6.0/poco-1.6.0.tar.gz
tar xvfz poco-1.6.0.tar.gz
cd poco-1.6.0
mkdir -p cmake_build cmake_install/debug cmake_install/release
cd cmake_build
cmake3-gui ..
In the CMake 3 GUI, press Configure. In the new window, keep the default option Unix Makefiles and click on Finish. An error message should appear (which is fine), click Ok.
To build the Debug version, set the following:
CMAKE_BUILD_TYPE : Debug
CMAKE_INSTALL_PREFIX : the absolute path to "cmake_install/debug"
To get you started quickly with POCO, unckeck all the options, except for the following, they have to be enabled:
ENABLE_JSON
ENABLE_NET
ENABLE_UTIL
ENABLE_XML
POCO_STATIC
(You can consider the other options later if you need to...)
Quit the GUI, then build/install POCO:
make clean
make -j8
make install
Now, POCO should be installed in cmake_install/debug. To build/install the other versions, just do the same procedure, but replace Debug in CMAKE_BUILD_TYPE with Release, RelWithDebInfo or MinSizeRel (cf. CMake's doc) (also, you'll have to change the install directories)
Finally, you can use POCO in you C++ projects.
For instance, your CMakeLists.txt should look like this:
cmake_minimum_required(VERSION 3.0)
project(Email_Reader)
# define the project
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(SOURCE_FILES main.cpp)
add_executable(Email_Reader ${SOURCE_FILES})
# set the POCO paths and libs
set(POCO_PREFIX "/path/to/cmake_install/debug") # the directory containing "include" and "lib"
set(POCO_INCLUDE_DIR "${POCO_PREFIX}/include")
set(POCO_LIB_DIR "${POCO_PREFIX}/lib")
set(POCO_LIBS "${POCO_LIB_DIR}/libPocoNetd.a"
"${POCO_LIB_DIR}/libPocoUtild.a"
"${POCO_LIB_DIR}/libPocoJSONd.a"
"${POCO_LIB_DIR}/libPocoXMLd.a"
"${POCO_LIB_DIR}/libPocoFoundationd.a"
"pthread")
# set the include path for the app
target_include_directories(Email_Reader PRIVATE "${POCO_INCLUDE_DIR}")
# link the app against POCO
target_link_libraries(Email_Reader "${POCO_LIBS}")
My CMakeLists.txt for using Poco looks like this:
cmake_minimum_required(VERSION 3.10.0)
project(MyProject VERSION 0.1.0)
find_package(Poco REQUIRED COMPONENTS Foundation Net Zip )
add_executable(my_exe main.cpp)
target_link_libraries(my_exe PUBLIC Poco::Foundation Poco::Zip Poco::Net)
This configuration automatically add the needed include directories and libraries.
The Foundation component is mandatory, it seems it provides the include directories.
Don't add Poco to target_link_libraries, the linker will then look for a 'Poco' library.

How to use cmake find_package() with a local copy of the package?

I'm trying to make a project that has both ZLIB and LIBPNG (and other libraries). LibPNG's CMakeLists.txt file has this in it: find_package(ZLIB REQUIRED) It's stock code that comes with it and I don't want to change it.
I'm building on Windows (Visual Studio). This is a cross-platform application (Windows, Mac, Linux and Mobile devices) I cannot rely on /usr/lib versions of any libraries. So I'm building them all with my project together.
I can't get LibPNG to build unless I hack this up. In an upper-level CMakeLists.txt file, I put this in there:
ADD_SUBDIRECTORY(contrib/${CUSTOM_ZLIB_LOCATION})
SET(ZLIB_FOUND ON CACHE BOOL "Yes")
SET(ZLIB_INCLUDE_DIR ${CMAKE_BINARY_DIR}/contrib/${CUSTOM_ZLIB_LOCATION} {CMAKE_SOURCE_DIR}/contrib/${CUSTOM_ZLIB_LOCATION})
SET(ZLIB_LIBRARY zlib CACHE STRING "zlib library name")
This satisfies find_package(ZLIB REQUIRED) But I think this is a hack. Is there some straight forward way to build the local copy of zlib without all the 3 extra lines?
I only added this line at the beginning (at least before find_package(ZLIB REQUIRED)) and it worked for me.
set(ZLIB_ROOT <zlib folder here>)
But others may need doing something like:
if (CMAKE_VERSION VERSION_GREATER 3.12 OR CMAKE_VERSION VERSION_EQUAL 3.12)
# Enable find_package uses of <PackageName>_ROOT variables.
cmake_policy(SET CMP0074 NEW)
endif()
set(ZLIB_ROOT <zlib folder here>)
We set the policy to NEW.
The OLD behavior for this policy is to ignore <PackageName>_ROOT variables.
CMake version 3.22.1 warns when the policy is not set (and defaults to OLD behavior).