Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] allow arbitrary CMake code to be appended to cmake_find_package generated find modules #7217

Closed
1 task done
TimSimpson opened this issue Jun 19, 2020 · 6 comments

Comments

@TimSimpson
Copy link
Contributor

Motivation

As of today, there are people out who use CMake to generate package config files, save them to their Conan packages, and then consume them later using the cmake_paths generator. This blog post represents a good summary of the thinking behind this. A big draw is the ability to use any custom targets CMake could have possibly generated from later dependent CMake projects consuming that dependency.

cmake_find_package has always been recommended by the Conan community as using the cmake_paths generator along with CMake generated package info introduces a parallel dependency graph which can be inconsistent and lead to surprising results. However, because it is not always possible to mimic the targets created by native CMake package configs from within Conan using only the cmake_find_package generator there remains a subculture of Conan users who continue to rely on cmake_paths.

As @memsharded was kind enough to explain to me recent new features in Conan make it easier to achieve parity with CMake targets found in the wild using only the Conan generators. However, there are still limitations, such as the inability to create a target namespace in CMake that differs from the argument name to find_package.

This made me think, what if users could just add CMake code to the generated CMake FindXYZ files?

In most cases all that's really needed is to create some aliases to whatever targets Conan has already generated, and then let other CMake code consuming the packages use these aliases. Meanwhile Conan generated CMake code could continue to use the same generated targets as before.

There may be some objections that other build tools would not know about this information because it won't be represented in Conan's model. But that's beside the point- the goal is to just let people rig up whatever interfaces they need for use in CMake exclusively.

One possible solution

This branch on my fork is a proof of concept which clumsily adds a new property to cpp_info called cmake_find_package_coda that allows a user to add a code to the bottom of the FindXYZ files generated by CMake (I'm sure there may be cleaner ways of doing this that may be more idiomatic but I'm not familar with the internals of Conan - my goal is to talk about the idea not necessarily this implementation).

This project shows how it could be used. The project's package_info method uses cmake_find_package_coda like so:

        self.cpp_info.cmake_find_package_coda = """

add_library(lp3::main INTERFACE IMPORTED)
set_target_properties(lp3::main PROPERTIES INTERFACE_LINK_LIBRARIES lp3-main::lp3-main)

        """

The test_package then uses only the cmake_find_package generator but then goes on to use lp3::main instead of lp3-main::lp3-main.

In theory, any additional Conan generated Cmake scripts (such as a dependency which itself used lp3-main somehow) would refer to the targets lp3-main::lp3-main. But the non-generated CMake scripts could could choose to use lp3::main if desired.

Another motivation

Outside of Conan, CMake has no rules or standards defining the names of targets exported by calls to find_package, so the ecosystem is sort of the wild west. I think Conan packages could benefit if they attempted to emulate multiple possible variations of common targets for packages to ease adoption.

For example, vcpkg's SDL2-image package can be used from CMake by callingfind_package(sdl2-image) and then using the target SDL2::SDL2_image. Ignoring for a minute the fact that the BinCrafter's package currently has to be used with find_package(sdl2_image) it would be nice if Findsdl2_image could add a coda making an alias SDL2::SDL2-image which refered to sdl2_image::sdl2_image. This could aid the transition path for new users to Conan who might be testing it out with large CMake scripts that are already using the targets provided by vcpkg.

@TimSimpson TimSimpson changed the title [feature] Feature Request: allow arbitrary CMake code to be appended to cmake_find_package generated find modules [feature] allow arbitrary CMake code to be appended to cmake_find_package generated find modules Jun 19, 2020
@danimtb danimtb self-assigned this Jun 22, 2020
@danimtb
Copy link
Member

danimtb commented Jun 22, 2020

Hi @TimSimpson and thanks for reporting this.

In Conan 1.26 we added support for Components in cmake_find_package generator. You are now able to define the targets and the names for those targets in the cpp_info model. See #7108

Regarding custom CMake code (or any other). We introduced some releases back the self.cpp_info.build_modules https://docs.conan.io/en/latest/reference/conanfile/attributes.html#cpp-info where you can define the paths to the CMake scripts in your package and cmake/cmake_find_package/cmake_find_package_multi generators will include() those files the in the files generated by those generators. That way you are able to reuse any kind of build utility (normally CMake macros) that the library authors provide users with.

I think those you feature should cover the use cases you are proposing here. WDYT?

@TimSimpson
Copy link
Contributor Author

Thanks for your response @danimtb. You are correct, build_modules already provides a way to do this.

The only bummer - sort of - is that the build modules are included before_ the targets are defined in the generated find module files, rather than at the end of the file, so there's no way to create clones of the targets by referencing the actual targets Conan creates. However it's possible to reference the variables Conan will use to create those targets, which in the end it probably works out just as well.

For anyone who finds this in Google, this commit shows how to use build_modules to create targets with any crazy name you want. The important part is the following:

function(create_target_from_conan_name cmake_name conan_name)
    add_library(${cmake_name} INTERFACE IMPORTED)

    if(${conan_name}_INCLUDE_DIRS)
        set_target_properties(${cmake_name} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
                              "${${conan_name}_INCLUDE_DIRS}")
    endif()
    set_property(TARGET ${cmake_name} PROPERTY INTERFACE_LINK_LIBRARIES
                 "${${conan_name}_LIBRARIES_TARGETS};${${conan_name}_LINKER_FLAGS_LIST}")
    set_property(TARGET ${cmake_name} PROPERTY INTERFACE_COMPILE_DEFINITIONS
                 ${${conan_name}_COMPILE_DEFINITIONS})
    set_property(TARGET ${cmake_name} PROPERTY INTERFACE_COMPILE_OPTIONS
                 "${${conan_name}_COMPILE_OPTIONS_LIST}")
endfunction()

create_target_from_conan_name(lp3::main lp3-main)

@danimtb
Copy link
Member

danimtb commented Jun 23, 2020

regarding the name of the tragets, have you tried something like:

def package_info(self):
    self.cpp_info.names["cmake_find_package"] = "lp3"
    self.cpp_info.components["main"].names["cmake_find_package"] = "main"  # explicit, not needed
    ...

I think build_modules in combination with components and the names should give you all the flexibility

@GordonJess
Copy link

The only bummer - sort of - is that the build modules are included before_ the targets are defined in the generated find module files, rather than at the end of the file, so there's no way to create clones of the targets by referencing the actual targets Conan creates. However it's possible to reference the variables Conan will use to create those targets, which in the end it probably works out just as well.

@danimtb, is there any reason for including the build modules before targets are defined instead of after?

I'd like to set a custom property on targets and this doesn't seem possible with the current implementation.

@danimtb
Copy link
Member

danimtb commented Jun 24, 2020

@GordonJess I think there was no apparent reason for this order so we could consider adding the modules after the targets are created. Would you mind opening an issue for this case so we can continue the conversation there? Thanks 💯

@TimSimpson
Copy link
Contributor Author

regarding the name of the tragets, have you tried something like:

@danimtb if I go that route, the argument to CMake find_package would have to be lp3. What I'd like to do is use the namespace lp3:: from multiple conan packages. So, find_package(lp3-main) would introduce lp3::main, find_package(lp3-sdl) would introduce lp3::sdl, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants