conan-io / conan

Conan - The open-source C and C++ package manager
https://conan.io
MIT License
8.13k stars 969 forks source link

[question] packaging debug visualizers (natvis) #16916

Open vasama opened 2 weeks ago

vasama commented 2 weeks ago

What is your question?

I want to package a library containing Visual Studio natvis debug visualizers for its interface types. I could not find any documentation on how to approach this with Conan. This requires adding the natvis files as sources on the CMake target generated by CMakeDeps. Is there an existing way to achieve this in the package recipe?

Have you read the CONTRIBUTING guide?

memsharded commented 2 weeks ago

Hi @vasama

Thanks for your question.

This requires adding the natvis files as sources on the CMake target generated by CMakeDeps. Is there an existing way to achieve this in the package recipe?

At the moment there is not built-in direct way to do this. The packages can define in package_info() a self.cpp_info.srcdirs to indicate the consumers there are some source files there, but these files are not automatically appended to targets. The usual approach is that consumers know what to do with these files. Can you please elaborate a bit more how do you add sources to a CMake target and how the consumers will automatically use (or not) these sources?

I think the best approach could be to generate a CMake build module that manages to add those extra files to the targets, see for example https://docs.conan.io/2/examples/graph/tool_requires/use_cmake_modules.html. These modules are automatically included at the end of find_package() xxxx-config.cmake script, so they execute and could add information to targets.

vasama commented 2 weeks ago

Adding the .natvis file as an INTERFACE source on an IMPORTED INTERFACE library allows Visual Studio to see the visualizer in a dependent project:

cmake_minimum_required(VERSION 3.24)
project(test)

add_library(lib INTERFACE IMPORTED)
target_sources(lib INTERFACE lib.natvis)

add_executable(app app.cpp)
target_link_libraries(app PRIVATE lib)
memsharded commented 2 weeks ago

target_sources(lib INTERFACE lib.natvis)

But what is exactly the usage of these files in the consumer project? Are they compiled automatically in any consumer target that does a target_link_libraries() over the imported target that is created with find_package()? Or is it only for visualizing the file in the IDE?

vasama commented 2 weeks ago

The IDE uses them for debug visualisation and I believe may also embed them into .pdb files.

vasama commented 2 weeks ago

Having tested it now, MSVC does indeed embed them in the .pdb files generated for consumer projects.

memsharded commented 2 weeks ago

Thanks for the feedback.

Yes, I think the best approach could be to define a cmake_build_module that add the sources you want to the target. Besides the docs, I think this test here can be useful: https://github.com/conan-io/conan/blob/fb3983d188ed70b4639f2479a65b26de4abfb06a/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_build_modules.py#L9, it is a full example of adding information to the CMake targets (in the test case an alias) and how it is automatically injected to the consumers, I think the same approach can be used for adding sources. Please give this a try and let us know.

vasama commented 1 week ago

Okay, I made it work using a CMake build module. During the CMake configuration of the package, I generate the build module script at build/cmake_build_modules/my_script.cmake, where build is the CMake project binary directory. When the project is installed, that file is copied into package/cmake_build_modules/my_script.cmake. package_info finds all the CMake scripts in the package/cmake_build_modules/ directory and sets the cpp_info cmake_build_modules property accordingly. This works when the package is created, but not when the package is in editable mode. It's not clear to me how I can make package_info find the scripts when in editable mode.

memsharded commented 1 week ago

Great, happy that you were able to make it work.

This works when the package is created, but not when the package is in editable mode. It's not clear to me how I can make package_info find the scripts when in editable mode.

For the editable case, the location of the module at "build" time should be defined in the layout() method, something like:

def layout(self):
     ...
     self.cpp.source.builddirs = ["build/cmake_build_modules"]
vasama commented 1 week ago

Shouldn't that instead be:

def layout(self):
    ...
    self.cpp.build.builddirs = ["cmake_build_modules"]

In any case, that still leaves detecting the build modules. Currently I do this to find them in the package folder:

def package_info(self):
    ...

    # Find and add all scripts in package_folder/cmake_build_modules/
    cmake_build_modules_dir = os.path.join(self.package_folder, "cmake_build_modules")
    if os.path.isdir(cmake_build_modules_dir):
        cmake_build_modules = self.cpp_info.get_property("cmake_build_modules") or []

        for entry in os.scandir(cmake_build_modules_dir):
            cmake_build_modules.append(os.path.join(cmake_build_modules_dir, entry.name))

        if len(cmake_build_modules) > 0:
            self.cpp_info.set_property("cmake_build_modules", cmake_build_modules)

It's still not clear to me how I can make this work in editable mode.

memsharded commented 1 week ago

self.cpp.build.builddirs

It depends. If the modules are generated at build time, it is self.cpp.build. If they are already part of the source, it is self.cpp.source.

I think the issue is that the folder is relative to the package folder, not to the build-folder, this is why I used self.cpp.source.builddirs = ["build/cmake_build_modules"], with the build/ prefix. Did you try it?

cmake_build_modules = self.cpp_info.get_property("cmake_build_modules") or []

Not very clear why you are doing it, the cpp_info.get_property() should always be empty, isn't it? This is your package property, so not anyone else would be setting it.

It's still not clear to me how I can make this work in editable mode.

Maybe the best would be to try to put this in a full reproducible example, either a repo with some minimalistic code if you think you could easily provide it, or I can try to provide a test in the Conan test suite later.

vasama commented 1 week ago

I have made a small example here: https://github.com/vasama/conan-issue-16916

cd ./lib
conan create .

cd ../app
conan install .
cmake --preset $PRESET # My Conan profile produces an MSBuild solution
cmake --open $BUILDDIR # Open the solution in Visual Studio

Note the .natvis file in the app project: Screenshot 2024-09-07 115532

Note the visualization size=5 defined in the natvis file. Screenshot 2024-09-07 120340

memsharded commented 1 week ago

Hi @vasama

Thanks very much for putting together the code, that really helped.

I'd like to apologize, because I was mistaken and I gave confusing information. I was mixing the self.cpp.xxx.builddirs = ["cmake_build_modules"] with the set_property("cmake_build_modules"), which aim for a similar purpose, but not what you were looking for in this case. The right approach is using set_property().

I have done a PR in https://github.com/vasama/conan-issue-16916/pull/1, I'll comment the details there

memsharded commented 1 week ago

I tested both flows, both with conan create and also with the conan editable add lib

vasama commented 3 days ago

I made a related issue on the CMake issue tracker here, since the CMake support around .natvis files could use some improvement as well.

Since you added this to the 2.8.0 milestone, are you planning some Conan changes to address this?

memsharded commented 3 days ago

No, I don't think it is needed any change in Conan client at the moment. I just wanted to make sure that this ticket wasn't buried, so assigning it to 2.8 was the way. I think we might close the ticket now? Thanks for the feedback!