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

Viewed 3128

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:

  1. 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.
  2. Simply having it as a subfolder in my project makes it a risk of changing something I didn't intend to change.
  3. 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).

2 Answers

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.

Related