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

[bug] Redundant linkage when using both cmake and cmake_find_package generators #8538

Closed
Fefer-Ivan opened this issue Feb 22, 2021 · 9 comments

Comments

@Fefer-Ivan
Copy link
Contributor

Environment Details

  • Conan version: 1.33.1

Steps to reproduce

  1. Have a conanfile.txt with cmake and cmake_find_package generators
  2. Have a library that depends on specific components of another library. For example: self.cpp_info.requires = ["boost::system", "boost::date_time", "boost::regex"]
  3. Use find_package(mylib) and target_link_libraries(myexecutable mylib::mylib) in your CMakeLists.txt file
  4. Expect myexecutable to depend only on boost::system boost::date_time and boost::regex.
  5. Actually it will depend on all of enabled boost components.

Logs

cmake generator will produce following dependencies

set(_CONAN_PKG_LIBS_MYLIB_DEPENDENCIES CONAN_PKG::boost")

which means it links with whole boost, not only required components.

cmake_find_package generator will produce following dependencies

set(_mylib_DEPENDENCIES " Boost::system;Boost::date_time;Boost::regex")

which is great. It is exactly what I need.

But when linking with mylib::mylib from Findmylib.cmake I still get the whole boost.
That is because both conanbuildinfo.cmake from cmake generator and Findmylib.cmake from cmake_find_package generator define same target for internal use

CONAN_LIB::${package_name}_${_LIBRARY_NAME}${build_type}

But this target have different dependencies depending on file: whole boost in conanbuildinfo.cmake and only required boost components in Findmylib.cmake

I am forced to include conanbuildinfo.cmake before any find_package calls, because in conanbuildinfo.cmake target creating does not have if(NOT TARGET ${_LIB_NAME}) check.

Proposed solution is to use different target names in different generators to avoid overlap.

@memsharded
Copy link
Member

Hi @Fefer-Ivan

It is not fully clear why you need the conanbuildinfo.cmake. The find_package calls should have the targets there, and the cmake_find_package generators (and the new experimental CMakeDeps one designed to replace all of them) shouldn't be used together with the cmake generators. They are alternative generators. Can you clarify why you are using both?

@Fefer-Ivan
Copy link
Contributor Author

When we first introduced conan to our project, we used cmake generator. Recently we had to link with individual components of boost, so we enabled cmake_find_package generator and used it for boost only. We are now switching to cmake_find_package only.

But still it is very unexpected for generators to produce conflicting code. There are a lot of recipes in conan-center that use both generators. Also when I asked if it is okay to use both generators in slack (https://cpplang.slack.com/archives/C41CWV9HA/p1613400004222800), several people replied that it will work.

@solvingj
Copy link
Contributor

This is a bummer of a migration challenge. Thank you for reporting.

I read the responses and I don't know which recipes in Conan Center they were referring to which use multiple generators. Maybe @uilianries or @SSE4 can recall some examples, and possibly explain how they might work if such cases exist. If not, I think the people in slack were also somewhat speculating that it "should work in theory". Maybe it works in some cases but not others.

And I agree, Ideally, they could both be used together and not cause issue. But, of course expectations are subjective. It doesn't seem unexpected to me at all that these generators would produce conflicting code and duplicate linkage. It took mountains of effort by dozens of people over multiple years to get them to both be feature complete, redundant to each other, and virtually interchangeable. I'm quite sure nobody said at any point, that there was a strong requirement to have them work in harmony together and just "do the right thing" if they happened to encounter each other.

@SpaceIm
Copy link
Contributor

SpaceIm commented Mar 24, 2021

I read the responses and I don't know which recipes in Conan Center they were referring to which use multiple generators.

All the recipes in CCI use at least cmake generator when upstream build system is CMake, to properly inject several informations from profile.

Many CCI recipes use multiple generators in addition to cmake generator, to make upstream CMakeLists happy, cmake_find_package, cmake_find_package_multi, pkg_config, sometimes all of them.
Indeed, sometimes you HAVE to provide Find module file for several dependencies (like openssl, zlib, bzip2 etc, otherwise find_package will use built-in Find module files, not config files, because Find module files have precedence over config file), AND config files for others (because find_package is called with CONFIG or NO_MODULE keywords for example).

generators internally used by a recipe to build the libs, and generators used by consumers to consume this recipe are orthogonal.

@solvingj solvingj assigned memsharded and unassigned solvingj Mar 24, 2021
@solvingj
Copy link
Contributor

Reassigning to determine if we will try to take action on this issue or not.

@silvester747
Copy link

silvester747 commented Jun 14, 2021

I noticed that when depending on a Conan package that depends on a component the behavior is also not fully consistent. The build will link to the unused component, but you cannot copy it in the imports method of your Conan package.

Example:
Conan package A exposes A::A1 and A::A2, each with their own shared library.
Conan package B only depends on A::A1
Conan package C depends on B, but also relies on another component that requires TARGETS to be enabled (custom find_package).

C now links to both A::A1 and A::A2, but the following code in its Conanfile only copies the shared library for A::A1

    def imports(self):
        self.copy(pattern="*.dylib*", dst="lib", src="lib")
        self.copy(pattern="*.so*", dst="lib", src="lib")

@memsharded
Copy link
Member

I am trying to reproduce this issue, this is my effort so far:

def test_components_duplicated_mixing_generators():
    conanfile = textwrap.dedent("""
        from conans import ConanFile

        class Pkg(ConanFile):
            settings = "os", "arch", "compiler", "build_type"

            def package_info(self):
                self.cpp_info.components["comp1"].system_libs = ["system_lib1"]
                self.cpp_info.components["comp2"].system_libs = ["system_lib2"]
        """)
    t = TestClient()
    t.save({"conanfile.py": conanfile})
    t.run("create . dep/0.1@")

    conanfile = textwrap.dedent("""
        from conans import ConanFile

        class Pkg(ConanFile):
            settings = "os", "arch", "compiler", "build_type"
            requires = "dep/0.1"

            def package_info(self):
                self.cpp_info.requires = ["dep::comp1"]
                self.cpp_info.system_libs = ["system_lib3"]
         """)

    t.save({"conanfile.py": conanfile})
    t.run("create . pkg/0.1@")

    conanfile = textwrap.dedent("""
        from conans import ConanFile, tools, CMake
        class Consumer(ConanFile):
            requires = "pkg/0.1"
            generators = "cmake", "CMakeDeps"
            exports_sources = "CMakeLists.txt"
            settings = "os", "arch", "compiler", "build_type"

            def build(self):
                cmake = CMake(self)
                cmake.configure()
        """)

    cmakelists = textwrap.dedent("""
        set(CMAKE_C_COMPILER_WORKS 1)
        set(CMAKE_C_ABI_COMPILED 1)
        project(consumer C)
        cmake_minimum_required(VERSION 3.15)

        include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
        conan_basic_setup(TARGETS)

        find_package(pkg)
        get_target_property(tmp pkg::pkg INTERFACE_LINK_LIBRARIES)
        message("component libs: ${tmp}")
    """)

    t.save({"conanfile.py": conanfile, "CMakeLists.txt": cmakelists})
    t.run("create . app/0.1@")

    assert "$<$<CONFIG:Release>:system_lib3;dep::comp1;$" in t.out

So it is not raising the error. Obviously I am missing something, but not evident what it is, if someone has some hint, that would be great.

@silvester747
Copy link

Regarding my comment: I found that the 2 components had different libdirs. So the copy statements needed to be adjusted for different src directories. That may require a bit of thinking on how import can be improved with components.

Regarding the reproduction. I have a suspicion that the unintended link comes in through another CMake variable. It seems to go wrong when combining CONAN_PKG:: with find_package.

garethsb added a commit to garethsb/nmos-cpp that referenced this issue Aug 9, 2021
garethsb added a commit to garethsb/nmos-cpp that referenced this issue Aug 9, 2021
@memsharded
Copy link
Member

This issue was reporting the legacy CMake integration, which has been superseded by the new one in Conan 1.X https://docs.conan.io/1/reference/conanfile/tools.html, and removed in Conan 2.0: https://docs.conan.io/2/reference/tools/cmake.html. Closing this ticket as outdated.

Please update to use the modern CMake integration, and please don't hesitate to open new tickets as necessary. Thanks!

@memsharded memsharded closed this as not planned Won't fix, can't repro, duplicate, stale Aug 25, 2023
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

5 participants