cmake: add "d" suffix for debug build of static library - c++

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.

Related

How to determine current build type of visual studio in CMakeList.txt

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.

How to include source of external library for IntelliSense in a CMake project?

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.

What's the ideal cmake installation directory structure for a relocatable multiple platform installation?

CMake install takes destination dirs, often using GNUInstallDirs to load standard values for the destination names. For example:
include(GNUInstallDirs)
install(TARGETS Foo
EXPORT Foo
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
However it doesn't provide different paths built for different platforms, or architectures. I've been installing to a platform specific folder within my project via CMAKE_INSTALL_PREFIX like this:
CMAKE_INSTALL_PREFIX=dist/${CMAKE_SYSTEM_NAME}/${CMAKE_SYSTEM_PROCESSOR}
This has some problems though:
It duplicates the includes which are the same across platforms.
If I install the different platforms to the same root but change the lib dir, cmake wouldn't find the cmake config targets in lib/non/standard/path/cmake/FooConfig.cmake
And Module mode searches also don't find the libraries properly, which is problematic when MODULE mode searches are done. This can happen if the library happens to also have a cmake module which requires every find_package to have to specify "CONFIG" which starts to get weird, especially when 3rd party libraries are told to consider this installation directory for a common dependency, and don't specify CONFIG in the find_package, because why would they?
I'm looking for a structure that works with find_package module mode and config mode; something like this:
<install_prefix>/
include/
foo/
foo.h
lib/
<PLAT x ARCH x CONFIG>/
cmake/
foo/
FooConfig.cmake
libFoo.a
Goal is:
libs can be co-installed for different platforms, architectures and configs
includes can be shared
finding the includes should work in typical module mode searches
should just work with vanilla find_package(Foo REQUIRED), but for the appropriate platform and arch.
Considering I'd be installing mostly 3rd party libraries here, if this can be done by overriding the vars from GNUInstallDirs, it'd probably work for many libraries. The rest I guess would either have to be edited or I just give up and use separate installation dirs that include platform and arch.
I'll post as far as I've gotten so far, and maybe it'll be useful. If someone has missing pieces I'll update this.
The documentation describing the search order is here:
https://cmake.org/cmake/help/v3.12/command/find_package.html?highlight=%3Cprefix%3E
There are quite a few supported permutations, so I'll start by eliminating some:
Ignore the Apple variations because that's for frameworks.
Ignore the Windows only paths, because it's not really idiomatic for any other platform.
Finally each has variations related to where the cmake script are found. I'll just pick the .../cmake/<name>*/ style.
That leaves (**extra space added to show similarity):
<prefix>/ (lib/<arch>|lib*|share)/cmake/<name>*/ (U)
<prefix>/<name>*/(lib/<arch>|lib*|share)/cmake/<name>*/ (W/U)
The docs state that certain search paths are meant for certain platforms, but technically I think all of the search paths are tried. It's just a matter of what's idiomatic for the platform. As such, however, you can't use the different styles as platform differentiator. In fact, this should mean the Unix option is also valid for Windows.
As you can see above the only difference for the Windows friendly Unix format is an extra prefix for the name. Either are valid choices, so for now I'll just refer to the Unix only style as it fits my preference. Finally, I don't really care about the "share" folder, because we're talking about C/C++ libraries.
So finally we're down to these 2 choices:
<prefix>/lib/<arch>/cmake/<name>*/
<prefix>/lib*/cmake/<name>*/
Option 1: Multi-arch
Paths with lib/<arch> are enabled if the CMAKE_LIBRARY_ARCHITECTURE variable is set. CMAKE_<LANG>_LIBRARY_ARCHITECTURE states:
If the <LANG> compiler passes to the linker an architecture-specific
system library search directory such as <prefix>/lib/<arch> this
variable contains the <arch> name if/as detected by CMake.
I know this is more specifically for certain distros that support multiarch, and is meant to be set automatically. However, I can't quite figure out if this is something that can easily be leveraged with cmake to achieve the goal here; what would you set to control this in cmake? On a multiarch system, libraries Foo and Bar could look like:
<prefix>/lib/x86_64-linux-gnu/
foo-1.1/
cmake/FooConfig.cmake
bar/
cmake/BarConfig.cmake
foo-1.1.so
foo-1.1.lib
foo-1.1.dll
foo-1.1.dylib
bar.so
bar.lib
bar.dll
bar.dylib
If we can control what the multiarch value is, this option could easily be used for other platforms like: Darwin_x86_64, Windows_x86_64, etc. And it would probably be compatible with find_package module mode where the includes would all be found, without needing config mode to redirect to some non-standard dir.
Option 2: lib variations
A partial solution is to keep the platforms completely separate in the prefix, but at least the 64-bit and 32-bit architectures can be combined just splitting the libs.
lib* includes one or more of the values lib64, lib32, libx32 or lib
(searched in that order).
Paths with lib64 are searched on 64 bit platforms if the
FIND_LIBRARY_USE_LIB64_PATHS property is set to TRUE.
Paths with lib32 are searched on 32 bit platforms if the
FIND_LIBRARY_USE_LIB32_PATHS property is set to TRUE.
Paths with libx32 are searched on platforms using the x32 ABI if the
FIND_LIBRARY_USE_LIBX32_PATHS property is set to TRUE.
The lib path is always searched.
This at least partially helps. I'd continue to use lib as 64-bit, and then just use lib32 for 32-bit needs.
If you expect user to set some standard directory as CMAKE_PREFIX_PATH, like
/usr/local # installation prefix
but your project installs things into non-standard, platform-specific subdirectories, like
/usr/local/linux/x86/ # actual root directory where things are installed
you may place FooConfig.cmake into standard subdirectory:
/usr/local/lib/FooConfig.cmake
but write it so it searches appropriate .cmake from platform-specific directory:
# File: FooConfig.cmake
# Location: lib/
#
# This is platform-independent script.
#
# Redirect configuration to the platform-specific script <system>/<cpu>/lib/FooConfig.cmake.
include(${CMAKE_CURRENT_LIST_DIR}/../${CMAKE_SYSTEM_NAME}/${CMAKE_SYSTEM_PROCESSOR}/lib/FooConfig.cmake)
Platform-specific *Config.cmake script can be written in a usual way.
So, if a user will write
find_package(Foo)
in an environment set for Linux/x86, it will setup imported targets for that platform.

Can CMake find_package be "common dependency version aware"?

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.

How do I write system-independent code when there are paths involved?

Say I am creating a project that uses a certain library and I have to provide the path for that library while linking. In the command line or makefile I might have:
g++ ... -L/path/to/mylibrary
I'm also going to send this project to someone else who wants to use it. The path on their system might not necessarily be the same as mine. They could be using a different file path all together.
How do I make sure that the path to the library works for both my computer and the recipient of my project?
This is the role of a build system or build configuration tool. There are many of those around. The main one is probably CMake as it has a very extensive feature set, cross-platform, and widely adopted. There are others like Boost.Jam, autoconf, and others.
The way that these tools will be used is that they have automated scripts for looking into the file-system and finding the headers or libraries that you need, i.e., the dependencies required to compile your code. They can also be used to do all sorts of other fancy things, like checking what features the OS supports and reconfiguring the build as a consequence of that. But the point is, you don't hard-code any file-paths into the build configuration, everything is either relative to your source folder or it is found automatically by the build script.
Here is an example CMake file for a project that uses Boost:
cmake_minimum_required (VERSION 2.8)
project (ExampleWithBoost)
find_package(Boost 1.46 COMPONENTS thread program_options filesystem REQUIRED)
# Add the boost directory to the include paths:
include_directories(SYSTEM ${Boost_INCLUDE_DIR})
# Add the boost library directory to the link paths:
link_directories(${Boost_LIBRARY_DIRS})
# Add an executable target (for compilation):
add_executable(example_with_boost example_with_boost.cpp)
# Add boost libraries to the linking on the target:
target_link_libraries(example_with_boost ${Boost_LIBRARIES})
The find_package cmake function is simply a special script (specialized for Boost, and installed with CMake) that finds the latest version of boost (with some minimal version) installed on the system, and it does so based on the file-name patterns that the library uses. You can also write your own equivalents of find_package, or even your own package finders, using the functions that CMake provides for searching the file system for certain file-name patterns (e.g., regular expressions).
As you see, the build configuration file above only refer directly to your source files, like "example_with_boost.cpp", and it's only relative to the source folder. If you do things right, the configuration scripts will work on virtually any system and any OS that CMake supports (and that the libraries you depend on support). This is how most major cross-platform projects work, and when you understand how to work with these systems, it's very powerful and very easy to use (in general, far easier to use and trouble-free than build configurations that you do by point-and-click within IDE menus like in Visual Studio).
You can use premake that generates cross platform makefiles: Visual Studio, Gcc and others
http://industriousone.com/what-premake
CMake is another alternative
http://www.cmake.org/
I'm not sure if there's a single universal way of doing this, but people often provide different config files and let the main Makefile detect which one to include: linux.make, darwin.make, cygwin.make etc.
there are also various tools like CMake that allow to automate this, but all in all it's just scripting that hides the system-dependency from the developer.