I've recently added a module to a CMake project that depends on a library that I only have compiled against the release CRT. It looks like this in CMakeLists.txt:
IF(WIN32)
ADD_LIBRARY(mymodule MODULE ${MY_LIBRARY_FILES})
TARGET_LINK_LIBRARIES(mymodule libVendor)
INSTALL(TARGETS mymodule LIBRARY)
ENDIF(WIN32)
If I try to compile this module in MSVC with debug settings, the compile fails. So what I want to do is exclude it from being compiled and installed in the debug configuration. In the release configuration, it would be used as normal. Is it possible to do this with CMake?
What you can also do is exclude the target from the default build in a certain configuration:
SET_TARGET_PROPERTIES(mymodule PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD_DEBUG True)
You can't have a target that is left out of a configuration, but you can have a library that is empty (or nearly empty) due to conditional compilation of its source code. And you can link to another library in a configuration specific way using the "optimized" and "debug" keywords to target_link_libraries.
For example, in your library source files, you could do:
#ifdef _DEBUG
// ... Debug code, possibly just a dummy function if necessary, goes here
#else
// ... Release code, the real deal, goes here
#endif
Then, you can specify that you only link to libVendor in the Release build, by using the "optimized" keyword for target_link_libraries, like this:
if(WIN32)
add_library(mymodule ...)
target_link_libraries(mymodule optimized libVendor)
install(TARGETS mymodule LIBRARY)
endif()
The target_link_libraries documentation explains the use of these keywords, and also mentions that you can define IMPORTED targets to achieve per-configuration effects. In order to define IMPORTED targets, though, the library files must have already been built, and you'd have to point to them. So... conditional compilation is probably the easiest way to do what you want to do.
Related
This is my build command in CMD:
cmake --build . --config Debug
This Debug can sometimes be Release, or sometimes it is the default. And I have a code in my CMakeList.txt:
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_link_libraries(${PROJECT_NAME} PRIVATE LLUd wstp64i4)
else()
target_link_libraries(${PROJECT_NAME} PRIVATE LLU wstp64i4)
endif()
I think its grammar is fine. But it is a great pity that Visual Studio(multi-config generators) does not recognize the CMAKE_BUILD_TYPE variable as the document. How should I change it?
I have found a similar topic here, but it seem don't work for me.
This is all you should need:
find_package(LLU REQUIRED PATH_SUFFIXES LLU)
target_link_libraries(MyTarget PRIVATE LLU::LLU)
If the above isn't working, you should ask about that error, not your attempted workaround.
The code you show is broken on several levels. First, the value of CMAKE_BUILD_TYPE should never be used to check the active build type because it is not set when using a multi-config generator like Visual Studio. Every in-project use of CMAKE_BUILD_TYPE is a code smell. It is meant to be set by the person building your project (maybe you, maybe not) as a cache variable from the outside.
Second, what you are actually trying to do is to pick a different physical (i.e. on disk) library for Debug mode versus Release (and other *Rel*) mode(s). But this is handled by CMake already. You shouldn't have to do what you're attempting.
I'm assuming that LLU is the Wolfram LibraryLink Utilities. If so, you should read the documentation which shows very nearly the same code I gave above. Notice that LLU::LLU is an imported target. Unless Wolfram's LLU package is broken, this will handle per-config library selection automatically. If it is broken, you should complain to the developers, loudly.
The moral of the story is, as always, to link to imported targets. If what you're linking to doesn't have :: in its name, you should be wary.
Here's a little background on why this is the case:
Imported targets that are generated by CMake's package export mechanism have the following properties set on them. These properties control CMake's view of which physical libraries should be used in which build types:
IMPORTED_LOCATION_<CONFIG> - This property names the path to the physical library that is to be used in the given configuration.
IMPORTED_CONFIGURATIONS - This property contains a list of the configurations that have been imported for the given target.
MAP_IMPORTED_CONFIG_<CONFIG> - This is a per-config property that maps <CONFIG>, which should not be present in IMPORTED_CONFIGURATIONS, to a second config that is present in IMPORTED_CONFIGURATIONS. The physical library from the second configuration will be used.
Regarding point (3), some packages forget to map MinSizeRel and RelWithDebInfo to Release. If you are getting an error in these configs (but not in Release) you can add the following code to your project before importing any packages:
set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO Release)
set(CMAKE_MAP_IMPORTED_CONFIG_MINSIZEREL Release)
This happens with LLVM's broken CMake packages. Most of the time, though, you don't have to worry about any of this.
I am working on a C++ application in Visual Studio, using the Visual Studio's CMake project template.
To build my application I only need the header and the external library and I can link it like this:
# CMakeList.txt : CMake project for myapp, include source and define project specific logic here.
cmake_minimum_required (VERSION 3.8)
# Add source to this project's executable.
add_executable (myapp "myapp.cpp" "myapp.h")
# Add TIFF library
set(TIFF_INCLUDE_DIR "C:\\libs\\tiff\\out\\install\\x64-Debug\\include")
set(TIFF_LIBRARY "C:\\libs\\tiff\\out\\install\\x64-Debug\\lib\\tiffd.lib")
find_package(TIFF REQUIRED)
target_link_libraries(myapp PRIVATE TIFF::TIFF)
So far, so good. I can use the tiff library to open, read, write tiff files, etc., and IntelliSense is catching up with declarations in the header files. But, I would like IntelliSense to also be aware of the full source code of the TIFF library. For example, if I am in myapp.cpp and I ctrl+click on TIFFOpen it opens the header corresponding to TIFFOpen, but when I ctrl+click TIFFOpen in the header file, it doesn't go to the corresponding source file, which is the normal behaviour for source files in myapp. This is understandable, since I never told Visual Studio where to find the source files of the external library.
CMake doesn't need to know where the source files of the external libraries are, since it won't build the external library, therefore I guess I don't/shouldn't change anything in CMakeLists.txt
One option (I haven't tried yet, but I'm fairly sure it would work), would be to just include the entire tiff library as a sub-project of myapp. I do have some problems with this solution though:
The external library is not conceptually an integral part of the project, and I don't plan to modify the external library. This is more of a principle issue.
Simply having it as a subfolder in my project makes it a risk of changing something I didn't intend to change.
I don't want to rebuild the external library when I do a rebuild all. I know Visual Studio / CMake is smart enough to figure out that nothing changed and doesn't rebuild, but I would rather have Visual Studio / CMake not even try.
The way I see it, I have to set the directory with the source files somewhere in Visual Studio settings, but still related to the project. My best guess is that the .vs/ProjectSettings.json is the file I need to edit somehow, but honestly, I have no clue.
Alternatively, maybe I could write some command in CMakeLists.txt that doesn't do anything, but triggers the IntelliSense to look in the folder with source files. Again, I have no clue how should I go about this.
In a nutshell, I want IntelliSense to see all source files of an external library, the same way it sees the source files of myapp, without including all source files of the external library as a sub-project of myapp. How should I go about it, if even possible?
If relevant, I use Visual Studio 2019 Community and the CMake it comes with it (3.15).
Regarding your last comment, writing code in comment section is inconvenient so I'll just post it here although not an answer.
libtiff-4.pc is for pkg-config, not cmake, and find_package() can't deal with it directly on Windows and would take some work if you really want to. It might be easier to just write everything manually. Remember to set the tiff.lib and tiffd.lib according to your configuration. You can use CMAKE_BUILD_TYPE variable and if() command such as:
# set build type to release if not specified
if(NOT CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "")
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build Type" FORCE)
endif()
# switch lib to link according to build type
if(CMAKE_BUILD_TYPE STREQUAL "Release")
# for release
set(TIFF_LIBRARY "<your_path_to_installed_lib>/tiff.lib")
else()
# for debug. If you have other configs like relwithdebinfo you can add more
set(TIFF_LIBRARY "<your_path_to_installed_lib>/tiffd.lib")
endif()
Also, remove find_package() and use target_link_libraries() and target_inlude_directories():
set(TIFF_INCLUDE_DIR "<your_path_to_installed_headers>/include")
target_link_libraries(myapp PRIVATE ${TIFF_LIBRARY})
target_include_directories(myapp PRIVATE ${TIFF_INCLUDE_DIR})
You can also skip setting TIFF_LIBRARY and TIFF_INCLUDE_DIR and pass the string directly if you like.
I use Visual Studio a lot and it's my favorite IDE. But package management with cmake on Windows is not as smooth as Linux. Always remember to set environment variables after compiling and installing external libraries.
Normally find_package() will look for a system environment variable named <libname>_DIR (for example TIFF_DIR, which is not found in your case), which is used to store path to installed lib, then look for <libname>Config.cmake and <libname>ConfigVersion.cmake in that folder (and would fail for TIFF since it doesn't have them).
It also searches other places, check https://cmake.org/cmake/help/v3.15/command/find_package.html?highlight=find_package for details.
So for those libs with cmake exported files, add a variable with correct name and value. And that's only for compiling.
If you want your application to run after compiling, you also need to add the path of installed lib's binaries (usually *.dll) to system's Path variable. In your case you should find something like tiff.dll and tiffd.dll after compiling and installation, add that folder to Path and you are good to go.
Actually, showing the source in the IDE is easy enough.
Just add the source with add_library(tiff_for_ide EXCLUDE_FROM_ALL ${SOURCES}) but don't link with it in the main program. You'll want to use a different target name for the library. For this you'd need the source in your project directly or as a submodule (if using Git, something else if available by you VCS).
Other options are using ExternalModule_Add; or FetchContent_MakeAvailable with the add_library as above, to avoid adding third party deps into the repository directly.
Now, just cross your fingers and hope that the IDE is not intelligent enough to restrict searching for sources that are linked against the target which compiles the current file. Or that it's intelligent enough to detect this situation but fallback to searching the whole project files anyway when it's linking against a binary.
I would like to implement a naming scheme for libraries similar to the one mentioned here: Library name for x32 vs x64
The CMakeLists.txt file is setup to create a static library
add_library(test test.h test.cpp)
After creating a visual studio solution from the cmake lists the project is set up in such a way that the debug library test.lib is written to /x64/Debug/test.lib and the release version is written to /x64/Release/test.lib. I would prefer to write them both to /lib/ but append a "d" to the debug version. The idea is to get
/lib/test.lib
/lib/testd.lib
and if possible have an additional suffix for 64 bit builds to get
/lib/test.lib
/lib/test64.lib
/lib/testd.lib
/lib/test64d.lib
Is there a straightforward way to do this?
Edit:
this can be used later nicely in the project using the libs like this:
Linking different libraries for Debug and Release builds in Cmake on windows?
Edit: I had problems removing the Debug and Release folders from the output, which can be fixed by this answer: How to not add Release or Debug to output path?
CMAKE_DEBUG_POSTFIX is used for appending the d for debug libraries:
set(CMAKE_DEBUG_POSTFIX d)
If you do not want to set this globally, you can also use the DEBUG_POSTFIX target property instead on selected libraries.
There is no corresponding feature for distinguishing 32/64 bit builds, but since it is impossible to mix those two in the same CMake configuration, you can easily distinguish those cases manually, e.g.
if(CMAKE_SIZEOF_VOID_P EQUAL 4)
set(ARCH_POSTFIX "")
else()
set(ARCH_POSTFIX 64)
endif()
add_library(my_lib${ARCH_POSTFIX} [...])
Or, if you want to use the same target name on the different architectures, set a variable like CMAKE_STATIC_LIBRARY_SUFFIX (there exist a whole bunch of them, so you can select the correct one for your target type and based on which output files you want to append a suffix to).
And since you also mentioned this answer for finding such libraries: Prefer using imported targets instead of the coarse-grained legacy debug and optimized qualifiers for target_link_libraries. Config file packages provide a convenient way of exposing such imported targets to your clients, and they also handle any suffix shenanigans automatically for you.
The Problem:
I'm in the process of redoing the make system in our legacy project, changing from its present arcane version to CMake. At the moment, I've got CMake treating the whole thing as one large CMake project, but our code base is so large that it's breaking most of the IDEs we throw at it.
We'd like to break it apart, and CMake's find_package "module mode" seems to be ideal for breaking it into "feature-sized" chunks. The prime candidate is a large chunk of the code that only needs to be maintained rarely, and even then its usually by another team. That would allow us to maintain the code, but not continuously recompile it when updating different code.
That said, this chunk of code uses Boost's shared pointer in the API, and while different versions of shared pointer probably will work together, I'd rather not take the chance. So, ideally, the package will be aware of what version of "boost" the system is using, what version of boost was used when the module was compiled, and be able to recompile -- or, at the very least, throw an error or warning in CMake -- if the two don't match.
So ... how does one go about ensuring that versions of common dependencies match in CMake find_package modules? The only thing I can think of is testing the appropriate VERSION variable, but that seems ... bulky. Is there something I'm missing?
Additional Information:
We're using CMake 3.5.1, but we can upgrade to 3.5.2 if that would make a difference. This project is actually a Software Product Line (q.v.), so we are planning to use more modern SPL Software Engineering techniques at some point in the future (yet another reason to choose CMake). The codebase is currently in Redhat Linux, but ideally the technique(s) would be cross-platform.
You may use config mode of the find_package for allow you modules to expose some internal properties to their user (root project).
If each your module provides library target, you may expose that target with property containing Boost version attached and list this property in a special COMPATIBLE_INTERFACE_STRING property.
Your root project will include modules via find_package() calls and will read these properties. When it will try to link libraries, provided by such modules, version compatibility will be automatically performed by CMake:
modA/CMakeLists.txt:
...
find_package(Boost)
add_library(modA_lib ...)
... # Link modA_lib with Boost
# Install modA_lib target and exports it for use in other project.
install(TARGETS modA_lib EXPORT modA_lib)
# Configured -config file is shown below
configure(modA-config.cmake.in modA-config.cmake)
install(EXPORT modA_lib
DESTINATION share/cmake/modA)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/modA-config.cmake
DESTINATION share/cmake/modA)
modA/modA-config.cmake.in:
include(#CMAKE_INSTALL_PREFIX#/share/cmake/modA/modA_lib.cmake) # Include file described library target
# Expose linked version of Boost via target's property.
set_property(TARGET modA_lib PROPERTY INTERFACE_BOOST_VERSION #Boost_VERSION#)
# Mark this property as compatibility requirement
set_property(TARGET modA_lib PROPERTY APPEND COMPATIBLE_INTERFACE_STRING BOOST_VERSION)
(modB is implemented in similar manner)
root/CMakeLists.txt:
find_package(modA) # This imports target modA_lib
find_package(modB) # This imports target modB_lib
add_executable(root_exe <...>)
# Boost version check will be performed here
target_link_libraries(root_exe modA_lib modB_lib)
Additionally, an executable created in the root project may request specific Boost version via setting appropriate property:
add_executable(root_exe <...>)
set_property(TARGET root_exe PROPERTY BOOST_VERSION <...>)
In this case it will be prohibited (by CMake) for its dependencies to use Boost library with other versions.
More info and usage examples see in CMake build system description.
I want to set up header-only C++ (or C) library projects, but can't find a clean way.
After some searches I've found that you can't set up a normal library using add_library to do this because it requires a compilable source file.
A way to do this would be to use add_custom_target instead, this way:
# Get all headers (using search instead of explicit filenames for the example)
file( GLOB_RECURSE XSD_HEADERS
*.hxx
)
add_custom_target( libsxsd SOURCES ${XSD_HEADERS} )
But that doesn't seem to work completely here as I can't see the sources in the project generated in VS2010. I don't know if it's a bug or if I'm doing it wrong or if there is a preferred way to do this.
Update: CMake will soon include a library target called INTERFACE that is ideal for header-only projects. This feature is currently in the master branch. Reference.
Using the command add_custom_target as you propose works for me (VS2010). The files are neatly listed within my project but it has the drawback that you can't define any "Additional Include Directories" with a custom target. Instead, I now use the following:
add_library(HEADER_ONLY_TARGET STATIC test1.hpp test2.hpp)
set_target_properties(HEADER_ONLY_TARGET PROPERTIES LINKER_LANGUAGE CXX)
This sets up your header-only project as a dummy archive target. Don't worry, no actual binaries will be generated if you should try and build it (at least not in VS2010 and Xcode 4). The command set_target_properties is there because CMake will otherwise complain that it cannot infer the target language from .hpp files only.
You can do this using the recent Interface Library feature:
add_library(mylib INTERFACE)
target_include_directories(mylib INTERFACE my_include_dir1 my_include_dir2)
This creates a library target without any source files, and adds the include directories to the INTERFACE_INCLUDE_DIRECTORIES property of the target. This means that any target that links to this library will get these directories as include paths (-I) when built.
For instance, to use the library with an executable target, just do:
add_executable(myexec ${MY_SOURCES})
target_link_libraries(myexec mylib)
I think what you are looking for is just adding an include directory using the
"include_directories" command for cmake.
When doing this, if it is a third party tool that you don't have control over, I would also add the "SYSTEM" flag.
So you command would look like something like this:
include_directories(SYSTEM ${GTEST_INCLUDE_DIRS})