CMakeLists.txt and two .cmake files - c++

I have two *.cmake files, lets say nacl.cmake and pnacl.cmake in my ./CMake/ folder. How do I cmake using the specific one using my CMakeLists.txt?

Make an option to let user switch between them:
option(USE_PNACL "Use PNaCl instead fo NaCl" TRUE)
if(USE_PNACL)
include(CMake/PNacl.cmake)
else()
include(CMake/Nacl.cmake)
endif()

They sound like two alternative toolchains. You can chose between different toolchains during configuration with the CMAKE_TOOLCHAIN_FILE option:
cmake -DCMAKE_TOOLCHAIN_FILE=<project-folder>/CMake/PNacl.cmake <project-folder>
If you don't specify a toolchain file, the build system will target the build machine.

Related

CMake not building a library when added as a subdirectory

I added the xgboost library as a git submodule of my project and I'm trying to add it to cmake as a subdirectory. Unfortunately it's not working. A simple hello world project with the following CMakeLists.txt replicates the error that I'm getting.
cmake_minimum_required(VERSION 3.2)
project(foo)
add_subdirectory(xgboost)
add_executable(${PROJECT_NAME} foo.cpp)
target_link_libraries(${PROJECT_NAME} xgboost)
After building the library there is nothing in the xgboost/lib directory so I get the following error.
clang: error: no such file or directory:
'/Users/.../myproject/xgboost/lib/libxgboost.dylib'
I think that the problem is generated in their CMakeLists file since they have two different targets. Maybe cmake is choosing the wrong target but I'm not familiar enough with cmake to figure it out. The following code is from xgboost's CMakeLists.
# Executable
add_executable(runxgboost $<TARGET_OBJECTS:objxgboost> src/cli_main.cc)
set_target_properties(runxgboost PROPERTIES
OUTPUT_NAME xgboost
)
set_output_directory(runxgboost ${PROJECT_SOURCE_DIR})
target_link_libraries(runxgboost ${LINK_LIBRARIES})
# Shared library
add_library(xgboost SHARED $<TARGET_OBJECTS:objxgboost>)
target_link_libraries(xgboost ${LINK_LIBRARIES})
set_output_directory(xgboost ${PROJECT_SOURCE_DIR}/lib)
#Ensure these two targets do not build simultaneously, as they produce outputs with conflicting names
add_dependencies(xgboost runxgboost)
My questions in order of importance are:
Is there any way to fix it without modifying xgboost's CMakeLists.txt file?
Is it reasonable to try to add xgboost to my project as a git submodule?
Is there any reason cmake is not instructing to build the library?
Note: There were several edits to this question since I tried to narrow down the problem and to provide more information.
(I would love to ask for few things beforehand in the comment section, but I have too low reputation to do so, so I will just give it a shot ;))
I have few suspects, and one of them is ${CMAKE_SOURCE_DIR} of the submodule's root CMakeLists.txt. Although the paths are set properly when you run that CMakeLists.txt alone, cmake gets confused the moment you add it as your subdirectory. Have you looked into another directories for your output binaries?
First I would suggest testing this hypothesis, and then I would suggest writing similar, but separate CMakeLists.txt file for xgboost library, and then substitute it in the project temporarily. Unfortunately the CMakeLists.txt filename is hardcoded and there is no possibility to have two files of that kind in one directory; so it seems that the answer to 1) is, that you rather have to change the file.
For the 2): as long as it does not require huge additional logic in your CMakeLists.txt, it makes sense. Other viable option is to create an install target, which you can use to install your xgboost library locally (using CMAKE_INSTALL_PREFIX(doc) variable), and then add the installation path to your CMAKE_LIBRARY_PATH(doc).

CMake: how to specify different CMakeFileList?

We created 3 different CMakeFileList files (CMakeFileList.engine, CMakeFileList.data and CMakeFileList.fep) for different build options within the same project. Does CMake support specifying CMakeFileList file as an argument? If not, what's the best way to accomplish our task by leveraging cmake? Any suggestion is appreciated.
In general I've done such kind of things by using the option() cmake command and providing just a single CMakeLists.txt file (what to build is decided inside according to the options, i.e. you can then also build everything in a single cmake/make run).
Example:
# the defaults (can be overridden on the command line)
option(BUILD_ENGINE "Build the Engine" ON)
option(BUILD_DATA "Build the Data" ON)
option(BUILD_FEP "Build the Fep" OFF)
if (BUILD_ENGINE)
# commands to build the Engine target or include(CMakeFileList.engine)
endif()
if (BUILD_DATA)
# commands to build the Data target or include(CMakeFileList.data)
endif()
if (BUILD_FEP)
# commands to build the Fep target or include(CMakeFileList.fep)
endif()
Then you can have everything in a single CMakeLists.txt and build what is needed each time, might it be multiple packages (if different than the defaults, you can switch on/off on the cmake command line). Or include the separate cmake lists as well (and just to make sure that they will work together if everything needs to be build).
Create main CMakeLists.txt file and conditionally use command include for use component-specific parts:
set(BUILD_TARGET "" CACHE STRING "Target to build")
if(BUILD_TARGET STREQUAL "engine")
include(${CMAKE_CURRENT_SOURCE_DIR}/CMakeFileList.engine)
elseif(BUILD_TARGET STREQUAL "data")
include(${CMAKE_CURRENT_SOURCE_DIR}/CMakeFileList.data)
elseif(BUILD_TARGET STREQUAL "fep")
include(${CMAKE_CURRENT_SOURCE_DIR}/CMakeFileList.fep)
else()
message(FATAL_ERROR "Incorrect BUILD_TARGET")
endif()

QtCreator CMake project - how to show all project files

I use QtCreator to open CMake project. Some directories apart from CMakeLists.txt contains only headers files *.h and for those directories QtCreator in the project tree view shows only CMakeLists.txt. How to fix that ? I need to see all project files from QtCreator.
Viewing project as a file system is not a solution at all cause your project editor settings for example would not apply.
And I do not like to add headers to executable target, cause they do not actually belong there. You effectively cripple the project file to work nicely with one particular IDE... not good.
The cleaner option IMHO would be:
FILE(GLOB_RECURSE LibFiles "include/*.hpp")
add_custom_target(headers SOURCES ${LibFiles})
As a bonus you get your includes shown in a separate folder.
(borrowed from https://cmake.org/pipermail/cmake/2012-August/051811.html)
I would suggest you switching your project view to File System. This would display a view where you could view any file you want:
You might want to split your project view into two by clicking the second to right button, if you still desire the Projects mode.
You should add header files to the list of your source files: add_executable(${Executable} ${Sources} ${headers})
You can use GLOB_RECURSE if have many header files:
FILE(GLOB_RECURSE INC_ALL "headers/*.h")
include_directories("headers")
add_executable(main "main.cpp" ${INC_ALL})
Don't forget to run CMake again (Build>Run Cmake).
Based on another thread asking the same question, I found a generic solution to the problem, working for all IDE's (at least tested with QtCreator and Visual Studio).
Can be found here : https://github.com/sauter-hq/cmake-ide-support
# \brief adds for the given target a fake executable targets which allows all
# headers and symbols to be shown in IDEs.
# \param target_name Which target properties should be added to the IDE support target.
function(target_add_ide_support target_name)
if (NOT TARGET ${target_name})
message(FATAL_ERROR "No target defined with name ${target_name}, cannot target_add_ide_support it.")
endif()
set (target_for_ide "${target_name}_ide_support")
if (NOT TARGET ${target_for_ide})
file(GLOB_RECURSE target_for_ide_srcs "*.h" "*.hpp" "*.hxx" "*.c" "*.cpp" "*.cxx")
add_executable(${target_for_ide} ${target_for_ide_srcs})
set_target_properties(${target_for_ide} PROPERTIES EXCLUDE_FROM_ALL 1 EXCLUDE_FROM_DEFAULT_BUILD 1)
endif()
get_target_property(dirs ${target_name} INCLUDE_DIRECTORIES)
target_include_directories(${target_for_ide} PRIVATE ${dirs})
endfunction(target_add_ide_support)
Usage is then for any targets in the CMakeLists, add the following call (can be made in top-most CMakeLists.txt after all add_subdirectory :
include(add_ide_support.cmake)
target_add_ide_support(some-target)
You can try CMakeProjectManager2. Code to display all files already propagated to upstream as a proof of concept. Concept applied but code can't be applied as-is for some reasons. So, simple wait feature in upstream.
There is a closed bug report about this issue: CMake project shows no files.
In that particular case the issue was with the chosen generator, Ninja, which is not well supported by QtCreator.
Please change that to "CodeBlocks - Ninja". Creator needs the CodeBlocks extra generator.
You should see a warning about that when hovering the kit (and the kit should have a warning icon in front of its name).
Using CodeBlocks - Ninja solved it for me too.
Overall, it may help to try up a few generators...

cmake - find_library - custom library location

I'm currently trying to get CMake running for my project (on windows). I want to use a custom location where all libraries are installed. To inform CMake about that path I tried to do that:
set(CMAKE_PREFIX_PATH D:/develop/cmake/libs)
But when I try to find the library with
find_library(CURL_LIBRARY NAMES curl curllib libcurl_imp curllib_static)
CMake can't find it.
When I set my prefix path to
set(CMAKE_PREFIX_PATH D:/develop/cmake/libs/curl)
... the library is located.
So my question is:
How can I configure CMake properly to work with a directory structore at a custom location which looks like that:
D:/develop/cmake/libs/
-> libA
-> include
-> lib
-> libB
-> include
-> lib
-> ...
-> include
-> lib
In "include" lie the public headers and in "lib" are the compiled libraries.
edit:
The current workaround for me is, to do this before i search for libraries:
set(CUSTOM_LIBRARY_PATH D:/develop/cmake/libs)
file(GLOB sub-dir ${CUSTOM_LIBRARY_PATH}/*)
foreach(dir ${sub-dir})
if(IS_DIRECTORY ${dir})
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH};${dir})
endif()
endforeach()
But that way the default module for boost wont find it until it because the directory structore of boost is a bit different.
boost -> include -> boost-1_50 -> *.hpp
When I move the content if "boost-1_50" to "include" the library can be found but that way it's not possible to handle multiple versions right?
The simplest solution may be to add HINTS to each find_* request.
For example:
find_library(CURL_LIBRARY
NAMES curl curllib libcurl_imp curllib_static
HINTS "${CMAKE_PREFIX_PATH}/curl/lib"
)
For Boost I would strongly recommend using the FindBoost standard module and setting the BOOST_DIR variable to point to your Boost libraries.
I saw that two people put that question to their favorites so I will try to answer the solution which works for me:
Instead of using find modules I'm writing configuration files for all libraries which are installed. Those files are extremly simple and can also be used to set non-standard variables. CMake will (at least on windows) search for those configuration files in
CMAKE_PREFIX_PATH/<<package_name>>-<<version>>/<<package_name>>-config.cmake
(which can be set through an environment variable).
So for example the boost configuration is in the path
CMAKE_PREFIX_PATH/boost-1_50/boost-config.cmake
In that configuration you can set variables. My config file for boost looks like that:
set(boost_INCLUDE_DIRS ${boost_DIR}/include)
set(boost_LIBRARY_DIR ${boost_DIR}/lib)
foreach(component ${boost_FIND_COMPONENTS})
set(boost_LIBRARIES ${boost_LIBRARIES} debug ${boost_LIBRARY_DIR}/libboost_${component}-vc110-mt-gd-1_50.lib)
set(boost_LIBRARIES ${boost_LIBRARIES} optimized ${boost_LIBRARY_DIR}/libboost_${component}-vc110-mt-1_50.lib)
endforeach()
add_definitions( -D_WIN32_WINNT=0x0501 )
Pretty straight forward + it's possible to shrink the size of the config files even more when you write some helper functions. The only issue I have with this setup is that I havn't found a way to give config files a priority over find modules - so you need to remove the find modules.
Hope this this is helpful for other people.
Use CMAKE_PREFIX_PATH by adding multiple paths (separated by semicolons and no white spaces). You can set it as an environmental variable to avoid having absolute paths in your cmake configuration files
Notice that cmake will look for config file in any of the following folders
where is any of the path in CMAKE_PREFIX_PATH and name is the name of the library you are looking for
<prefix>/ (W)
<prefix>/(cmake|CMake)/ (W)
<prefix>/<name>*/ (W)
<prefix>/<name>*/(cmake|CMake)/ (W)
<prefix>/(lib/<arch>|lib|share)/cmake/<name>*/ (U)
<prefix>/(lib/<arch>|lib|share)/<name>*/ (U)
<prefix>/(lib/<arch>|lib|share)/<name>*/(cmake|CMake)/ (U)
In your case you need to add to CMAKE_PREFIX_PATH the following two paths:
D:/develop/cmake/libs/libA;D:/develop/cmake/libB
There is no way to automatically set CMAKE_PREFIX_PATH in a way you want. I see following ways to solve this problem:
Put all libraries files in the same dir. That is, include/ would contain headers for all libs, lib/ - binaries, etc. FYI, this is common layout for most UNIX-like systems.
Set global environment variable CMAKE_PREFIX_PATH to D:/develop/cmake/libs/libA;D:/develop/cmake/libs/libB;.... When you run CMake, it would aautomatically pick up this env var and populate it's own CMAKE_PREFIX_PATH.
Write a wrapper .bat script, which would call cmake command with -D CMAKE_PREFIX_PATH=... argument.
You have one extra level of nesting.
CMAKE will search under $CMAKE_PREFIX_PATH/include for headers and $CMAKE_PREFIX_PATH/libs for libraries.
From CMAKE documentation:
For each path in the CMAKE_PREFIX_PATH list, CMake will check
"PATH/include" and "PATH" when FIND_PATH() is called, "PATH/bin" and
"PATH" when FIND_PROGRAM() is called, and "PATH/lib and "PATH" when
FIND_LIBRARY() is called.
I've encountered a similar scenario. I solved it by adding in this following code just before find_library():
set(CMAKE_PREFIX_PATH /the/custom/path/to/your/lib/)
then it can find the library location.

One click building my project with Cmake & Msbuild (vs2010) with a batch file

I have to make a one click build of a projet made for Cmake ( already works on Linux) with a batch file that downloads all the 3rd party libraries and compiles them. (win64)
If posible i dont want to change projets CMakeLists.txt.
I already build the project in VS2010 GUI. and i had to change the folowing:
a. had to change Configuration properties-C++-Command line: added /DWNT /D "CAD_STATIC"
b. had to add a long list of libraries in Configuration properties- Linker input- additional dependencies.
c. add library directories for those libraries
d. add include directories.
The project compiled and worked ok.
Now i need to make the same with only batch commands.
I already build the project file with cmake with:
cmake ..\projectsource -G "Visual Studio 10 Win64" -DGLEW_LIBRARY:FILEPATH=%myroot%\glew\trunk\lib\Release\glew.lib -DGLUT_glut_LIBRARY:FILEPATH=%myroot%\freeglut\trunk\lib\Release\freeglut.lib -DMKL_LIBRARIES:FILEPATH=%myroot%\mkl\em64t\lib\mkl_core.lib -DOpenCascade_INCLUDE_DIR:PATH=%myroot%\OpenCascade
Now i need a command like "devenv project.sln /useenv " that does the same as the stuff i did under #2.
I tried with a env include & lib like:
set "include=%myroot%\glew\trunk\include;%myroot%\freeglut\trunk\include;%myroot%\mkl\include;%myroot%\qt\include;%myroot%\OpenCascade\include\oce;%myroot%\trimo\src\CadModel;%include%"
set "lib=%myroot%\glew\trunk\lib\Release\*.lib;%myroot%\freeglut\trunk\lib\Release\*.lib;%myroot%\mkl\em64t\lib\*.lib;%myroot%"\qt\lib\*.lib;%myroot%\OpenCascade\Win64\lib\*.lib;%lib%"
All the help is very much appreciated. I'm stuck. Thanks
Edit:
I got another problem:
How can i unlink a library that gets linked in a project.sln automaticly by cmake?
will "lib=%myroot%\glew\trunk\lib\Release*.lib; link all the .lib files like u would get if u put all the libs in a vs2010 gui -Linker input- additional dependencies?
If you're already setting the required include and lib variables, then probably all that's missing is:
set "cl=/DWNT /DCAD_STATIC"
then you should be able to use
devenv project.sln /useenv /build
Note, you've also got libpath available to set search paths for the libraries if required.
Answers to further questions
I don't know of a way to do that.
No. I hadn't noticed you were doing that in your original question - sorry! The LIB env var sets search paths in which libs could be found, it's not for the full path to the actual lib itself.
What you're trying to achieve is exactly the sort of scenario at which CMake excels. You're fighting CMake here when it's probably the solution to the problems :-)
If I were you, I'd edit the CMakeLists.txt to include things like:
SET(MY_ROOT <path to %myroot%>)
FIND_LIBRARY(GLEW_LIBRARY glew ${MY_ROOT}/glew/trunk/lib/Release)
IF(NOT GLEW_LIBRARY)
MESSAGE(FATAL_ERROR "glew.lib not found in ${MY_ROOT}/glew/trunk/lib/Release")
ENDIF()
FIND_LIBRARY(GLUT_glut_LIBRARY freeglut ${MY_ROOT}/freeglut/trunk/lib/Release)
IF(NOT GLUT_glut_LIBRARY)
MESSAGE(FATAL_ERROR "freeglut.lib not found in ${MY_ROOT}/freeglut/trunk/lib/Release")
ENDIF()
FIND_LIBRARY(MKL_LIBRARIES mkl_core ${MY_ROOT}/mkl/em64t/Release)
IF(NOT MKL_LIBRARIES)
MESSAGE(FATAL_ERROR "mkl_core.lib not found in ${MY_ROOT}/mkl/em64t/Release")
ENDIF()
INCLUDE_DIRECTORIES(${MY_ROOT}/OpenCascad)
ADD_DEFINITIONS(-DWNT -DCAD_STATIC)
TARGET_LINK_LIBRARIES(<your target>
${GLEW_LIBRARY}
${GLUT_glut_LIBRARY}
${MKL_LIBRARIES}
<any other libs...>
)
This is all Windows-specific, and Release-specific. You could adapt the FIND_LIBRARY calls to cater for Unix/OSX options too, or you could wrap this in IF(WIN32) ... ENDIF() blocks.
You could also do FIND_LIBRARY calls for the Debug versions too if required (giving them different variable names) and adding them like:
TARGET_LINK_LIBRARIES(<your target>
optimized ${GLEW_LIBRARY}
optimized ${GLUT_glut_LIBRARY}
optimized ${MKL_LIBRARIES}
debug ${GLEW_LIBRARY_DEBUG}
debug ${GLUT_glut_LIBRARY_DEBUG}
debug ${MKL_LIBRARIES_DEBUG}
<any other libs...>
)
You'll also be able to remove whatever libraries you want from whatever targets you want by modifying the list of libs passed in the TARGET_LINK_LIBRARIES call(s).
If you want to grab all *.lib files in a directory, add something like this:
FILE(GLOB ALL_GLEW_LIBS "${MY_ROOT}/glew/trunk/lib/Release/*.lib")
TARGET_LINK_LIBRARIES(<your target> ${ALL_GLEW_LIBS})
If you do use the GLOB call, and you also need Debug and Release, be sure to prefix each list item with debug or optimized as appropriate, e.g.
FOREACH(GLEW_ITR ${ALL_GLEW_LIBS_RELEASE})
SET(ALL_GLEW_LIBS ${ALL_GLEW_LIBS} optimized ${GLEW_ITR})
ENDFOREACH()
FOREACH(GLEW_ITR ${ALL_GLEW_LIBS_DEBUG})
SET(ALL_GLEW_LIBS ${ALL_GLEW_LIBS} debug ${GLEW_ITR})
ENDFOREACH()
TARGET_LINK_LIBRARIES(<your target> ${ALL_GLEW_LIBS})
If all this is then catered for by CMake, you don't need to set any env vars in the batch script; you just do:
devenv project.sln /build