I've read on CMake's documentation that when calling add_executable, you can set the executable type to be Win32 by doing add_executable(target WIN32 source.cpp). I also know that you should use CMake generator expressions to check for build configurations like so:
target_compile_definitions(target PUBLIC
$<$<CONFIG:Debug>:DEBUG>
$<$<CONFIG:Release>:RELEASE>
)
However this won't work with add_executable. It treats it as a source file when I do add_executable(target $<$<CONFIG:Release>:WIN32> source.cpp) and so it fails. What is the correct way of doing setting the executable type to WIN32 only in release mode?
I am not really sure it makes sense given a WIN32 executable and a non-WIN32 executable do not have the same entry point, so the code would need to change as well.
Still, here is how you would do it on CMake side:
add_executable(target source.cpp)
set_target_properties(target PROPERTIES WIN32_EXECUTABLE $<CONFIG:Release>)
Key point is the WIN32 flag in add_executable is just a shortcut to set the WIN32_EXECUTABLE property.
Note: I cannot test this sample atm so it depends on me having read those links properly ;)
Related
I'm new to cmake, so up front, my apologies this question is too basic ..
Question is,
I have logs under #ifdef DEBUG which I want to print only for debug build.
Something like this ..
void func () {
// some code here
#ifdef DEBUG
print_log(...) // this portion should execute only for debug builds
#endif
// some code here
}
How can I achieve this with cmake ?
I have already looked at #ifdef DEBUG with CMake independent from platform and cmakelists debug flag not executing code within "ifdef DEBUG", suggestions here don't seem to work for me.
(project is on Linux platform)
Modern CMake uses a target-based approach which allows you to specify settings that are restricted to a target as opposed to being global (and affecting all targets). This then gives you the control to specify how the state from targets propagates transitively to dependent targets so as to reduce the visible scope of the state (include paths, library dependencies, compiler defines, compiler flags, etc) to dependent targets. The approach you decide to go with will depend largely on how complex your application is, for instance, how many targets (executable & libraries) exist in the system. The more complex the system the more benefits you in terms of reduced complexity and compile time that you will get from using the target-based approach. In the simplest case to set this up with the modern target based CMake approach you could use the following (where exe is the name of your executable:
add_executable(exe "")
target_sources(exe
PRIVATE
main.cpp
)
target_compile_definitions(exe
PRIVATE
# If the debug configuration pass the DEBUG define to the compiler
$<$<CONFIG:Debug>:-DDEBUG>>
)
Added set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") to CMakeLists.txt
Created Debug/ directory & cd Debug
cmake -DCMAKE_BUILD_TYPE="Debug" ../
Worked !
So, I've found really strange behaviour in CMake creating dependency on target_link_library..
It's hard to explain in one sentence, so here is a list of requirements (I hope this all will make sence in the end)
your project must have custom build type defined through CMAKE_CONFIGURATION_TYPES ('Tools' in this example)
you must have at least 3 targets:
executable (or simply main target) (test-exe in this example)
interface library which link to main target (this could be something other than INTERFACE library, but the next target must be linked to it via interface property only) (test-env in this example)
static library which links to the interface library with specific generator expression, which is depends on custom build type (something like that 'target_link_libraries(test-env INTERFACE $<$CONFIG:Tools:test-lib>)') (test-lib in this example)
Here is the code of the CMakeLists.txt file to make it little bit clearer:
cmake_minimum_required(VERSION 3.19.0 FATAL_ERROR)
project(multiconfiguration-test LANGUAGES CXX)
set(CMAKE_CONFIGURATION_TYPES Debug Release Tools)
set(CMAKE_CXX_FLAGS_TOOLS ${CMAKE_CXX_FLAGS_DEBUG})
set(CMAKE_EXE_LINKER_FLAGS_TOOLS ${CMAKE_EXE_LINKER_FLAGS_DEBUG})
set(CMAKE_STATIC_LINKER_FLAGS_TOOLS ${CMAKE_STATIC_LINKER_FLAGS_DEBUG})
add_library(test-env INTERFACE)
# EXCLUDE_FROM_ALL used to not build this target by default
add_library(test-lib STATIC EXCLUDE_FROM_ALL "test-lib.cpp")
target_link_libraries(test-env INTERFACE $<$<CONFIG:Tools>:test-lib>)
add_executable(test-exe "test-exe.cpp")
target_link_libraries(test-exe PRIVATE test-env)
(Files test-exe.cpp and test-lib.cpp are trivial, test-lib.cpp has a simple function, and test-exe.cpp declares this function and then calls it from main.)
When you would try to build this project with visual studio 2019/2017 generators (with "Tools" as configuration type of course: cmake --build . --config Tools), you will have next error:
LINK : fatal error LNK1104: cannot open file 'Tools\test-lib.lib' [<none_important_path_to_the_project>\test-exe.vcxproj]
And, what is important, you will not see in the terminal target test-lib being build.
So, what happened is target test-exe knows it must be linked against test-lib, but the build system doesn't know that target test-exe is dependent on target test-lib.
And now the most strange thing! If we will link this library like that: target_link_libraries(test-env INTERFACE $<$<CONFIG:Debug>:test-lib>) (so build type must be Debug), and still build project with Tools as a build type, you will see in the terminal that target test-lib is now building! (yes we have link error because test-exe can't find the function which is defined in test-lib, but this is at least expected)
So, what actually happens, the link flag of the target test-exe is correctly depends on the generator expression BUT, the actual build dependency, somehow, transforms any custom build type in this generator expression to the Debug.
Again this only happens with requirements I pointed up above, so it's not only the fault of generator expression, it's also connected to the interface dependency as well..
If we can't break any of the requirements, one possible solution will be to add direct dependency of test-lib target to test-env (like that: add_dependecies(test-env test-lib)), but this is not perfect, because even if we will use test-lib only then where is Tools as build type, we still will build this library each time, which can be undesired behavior.
I'm not really asking for solution here (but if you have one please share), I'm asking for the explanation why this even happening? Is this a bug or a really strange feature?
EDIT Small update I've found just now:
It must not even be the custom build type. The same bug can be encountered even with Release build type, so the final code can look as simple as this:
cmake_minimum_required(VERSION 3.19.0 FATAL_ERROR)
project(multiconfiguration-test LANGUAGES CXX)
add_library(test-env INTERFACE)
add_library(test-lib STATIC EXCLUDE_FROM_ALL "test-lib.cpp")
target_link_libraries(test-env INTERFACE $<$<CONFIG:Release>:test-lib>)
add_executable(test-exe "test-exe.cpp")
target_link_libraries(test-exe PRIVATE test-env)
and be build with next command: cmake --build . --config Release
Looks like a bug with the Visual Studio generator to me. I've just tested the Ninja Multi-Config generator both on Linux and on Windows and there cmake --build <build-dir> --config Release works just fine.
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...
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
So I've got a library I'm compiling and I need to link different third party things in depending on if it's the debug or release build (specifically the release or debug versions of those libraries). Is there an easy way to do this in Cmake?
Edit: I should note I'm using visual studio
According to the CMake documentation:
target_link_libraries(<target> [lib1 [lib2 [...]]] [[debug|optimized|general] <lib>] ...)
A "debug", "optimized", or "general"
keyword indicates that the library
immediately following it is to be used
only for the corresponding build
configuration.
So you should be able to do this:
add_executable( MyEXE ${SOURCES})
target_link_libraries( MyEXE debug 3PDebugLib)
target_link_libraries( MyEXE optimized 3PReleaseLib)
Somehow the answer from #Mike Willekes got CMake linking in the same target both release and debug for me :(
I only got this working by setting both configurations in one line, as suggested by #sakra in a related question - and doing so for every library that needed to be linked:
target_link_libraries ( app
debug ${Boost_FILESYSTEM_LIBRARY_DEBUG}
optimized ${Boost_FILESYSTEM_LIBRARY_RELEASE} )
target_link_libraries ( app
debug ${Boost_LOG_LIBRARY_DEBUG}
optimized ${Boost_LOG_LIBRARY_RELEASE} )
target_link_libraries ( app
debug ${Boost_PROGRAM_OPTIONS_LIBRARY_DEBUG}
optimized ${Boost_PROGRAM_OPTIONS_LIBRARY_RELEASE} )
# ...
I would like to add a few notes to the previous answers.
If you need to create a list of multiple files you want to link and store that in a cache variable then you need to add the optimized or debug specified before each and every library. This can be especially useful for larger makefiles/projects.
So for example you could do something like this:
set( MyFavLib_LIBRARIES
debug debug/module1.lib optimized release/module1.lib
debug debug/module2.lib optimized release/module2.lib )
target_link_libraries( app ${MyFavLib_LIBRARIES} )
What worked for me was to use $(Configuration) macro in a lib path provided to cmake.
So, assuming libs are stored in separate, correctly named folders, e.g.:
C:\boost\lib\Debug\libfoo.lib
C:\boost\lib\Release\libfoo.lib
You can then call cmake with:
cmake -G "Visual Studio 10 2010" -DBOOST_LIBRARYDIR=C:\boost\lib\$(Configuration)\libfoo.lib
That'll generate .vcxproj with Additional Dependencies including C:\boost\lib\$(Configuration)\libfoo.lib, what is evaluated to either C:\boost\lib\Release\libfoo.lib or C:\boost\lib\Debug\libfoo.lib depending on a chosen Configuration.
target_link_libraries with optimize and debug doesn't work for me. I follow the post of Mike Willekes, but release config also import debug library file in visual studio. Then, I use the following cmake code to solving this problem
add_library(BoostLib STATIC IMPORTED)
set_target_properties(BoostLib PROPERTIES
IMPORTED_LOCATION_DEBUG ${BoostLibPath}/debug/module1.lib
IMPORTED_LOCATION_RELEASE ${BoostLibPath}/release/module1.lib)
target_link_libraries(AppTarget BoostLib)