Repository: conan-io/conan

Issue:
[bug] 'cmake' generator does not set INTERFACE_LINK_DIRECTORIES
### Environment Details (include every applicable attribute)
  * Conan version: 1.46.0

### Steps to reproduce (Include if Applicable)

The error surfaced when trying to consume  `boost` with option `magic_autolink=True`. This option causes the link library list to be empty. Instead the boost headers include `#pragma comment` statements to select the libraries to link against. For this approach to work the libraries need to be found by the compiler.

Here are the locations for both cmake generators ([cmake](https://github.com/conan-io/conan/blob/e98a7590b544e738fdb927678d34821ccb78ad78/conans/client/generators/cmake_common.py#L166) and [cmake_find_package](https://github.com/conan-io/conan/blob/8f7ac4306cdf7fc4536728b07457b20895be0023/conans/client/generators/cmake_find_package.py#L179)). The latter correctly sets the `INTERFACE_LINK_DIRECTORIES` variable while the former is missing that part.



Discussion:
Hi @slietzau 

The ``cmake`` and ``cmake_find_package`` generators are no longer being developed, and efforts have been shifted to the new ``CMakeDeps`` and ``CMakeToolchain`` integration. Please have a look at those and report if necessary.
@memsharded Thanks for the quick response. `CMakeDeps` + `CMakeToolchain` don't work either.
can you clarify why you need that property? Aren't the targets defined by CMakeDeps working? I recommend starting with an example with ``conan new hello/0.1 -m=cmake_lib`` to report (The ``test_package/conanfile.py`` is using ``CMakeDeps`` there)
Thanks again. I just checked the boost recipe (because we use a modified version of the `boost` package). Turns out we clear the libs for all components:

```diff
- self.cpp_info.components[module].libs = module_libraries
+ self.cpp_info.components[module].libs = [] if self.options.magic_autolink else module_libraries
```

Anyway, the idea was to use an empty link list and let the compiler find the libraries. However that needs the `INTERFACE_LINK_DIRECTORIES` setting which is currently only supported by the `cmake_find_package` generator.

However, I guess it's not that good of an idea to remove all libraries from the link list. 

Here is a minimal working example based on `cmake_lib` template. I'm not yet experienced with conan `components` so that part might be more verbose than it needed to be. Worth noting is, that only the `cmake_find_package` generator for components includes the `INTERFACE_LINK_DIRECTORIES` property.

```diff
diff --git a/conanfile.py b/conanfile.py
index 83325ae..7811f22 100644
--- a/conanfile.py
+++ b/conanfile.py
@@ -42,4 +42,6 @@ class HelloConan(ConanFile):
         cmake.install()
 
     def package_info(self):
-        self.cpp_info.libs = ["hello"]
+        self.cpp_info.components['main'].includedirs.append('include')
+        self.cpp_info.components['main'].libdirs.append('lib')        
+        self.cpp_info.components['main'].libs = []
diff --git a/include/hello.h b/include/hello.h
index 9e87180..49f9d73 100644
--- a/include/hello.h
+++ b/include/hello.h
@@ -2,6 +2,7 @@

 #ifdef _WIN32
 #define hello_EXPORT __declspec(dllexport)
+#pragma comment(lib, "hello")
 #else
 #define hello_EXPORT
 #endif
diff --git a/test_package/CMakeLists.txt b/test_package/CMakeLists.txt
index c33c2a7..2df5beb 100644
--- a/test_package/CMakeLists.txt
+++ b/test_package/CMakeLists.txt
@@ -4,4 +4,4 @@ project(PackageTest CXX)
 find_package(hello CONFIG REQUIRED)

 add_executable(example src/example.cpp)
-target_link_libraries(example hello::hello)
+target_link_libraries(example hello::main)
```

With these changes the `CMakeDeps` generator cannot build the project. Switching to `cmake_find_package` builds the project.

Resolving patch:
diff --git a/conan/tools/cmake/cmakedeps/cmakedeps.py b/conan/tools/cmake/cmakedeps/cmakedeps.py
--- a/conan/tools/cmake/cmakedeps/cmakedeps.py
+++ b/conan/tools/cmake/cmakedeps/cmakedeps.py
@@ -13,7 +13,6 @@
 from conans.util.files import save
 
 
-
 class CMakeDeps(object):
 
     def __init__(self, conanfile):
diff --git a/conan/tools/cmake/cmakedeps/templates/target_configuration.py b/conan/tools/cmake/cmakedeps/templates/target_configuration.py
--- a/conan/tools/cmake/cmakedeps/templates/target_configuration.py
+++ b/conan/tools/cmake/cmakedeps/templates/target_configuration.py
@@ -26,12 +26,15 @@ def context(self):
         components_names = [(components_target_name.replace("::", "_"), components_target_name)
                             for components_target_name in components_targets_names]
 
+        is_win = self.conanfile.settings.get_safe("os") == "Windows"
+        auto_link = self.conanfile.cpp_info.get_property("cmake_set_interface_link_directories")
         return {"pkg_name": self.pkg_name,
                 "root_target_name": self.root_target_name,
                 "config_suffix": self.config_suffix,
                 "deps_targets_names": ";".join(deps_targets_names),
                 "components_names": components_names,
-                "configuration": self.cmakedeps.configuration}
+                "configuration": self.cmakedeps.configuration,
+                "set_interface_link_directories": auto_link and is_win}
 
     @property
     def template(self):
@@ -115,6 +118,7 @@ def template(self):
                      PROPERTY INTERFACE_LINK_LIBRARIES
                      $<$<CONFIG:{{configuration}}>:${{'{'}}{{pkg_name}}_LIBRARIES_TARGETS{{config_suffix}}}
                                                    ${{'{'}}{{pkg_name}}_OBJECTS{{config_suffix}}}> APPEND)
+
         set_property(TARGET {{root_target_name}}
                      PROPERTY INTERFACE_LINK_OPTIONS
                      $<$<CONFIG:{{configuration}}>:${{'{'}}{{pkg_name}}_LINKER_FLAGS{{config_suffix}}}> APPEND)
@@ -128,6 +132,13 @@ def template(self):
                      PROPERTY INTERFACE_COMPILE_OPTIONS
                      $<$<CONFIG:{{configuration}}>:${{'{'}}{{pkg_name}}_COMPILE_OPTIONS{{config_suffix}}}> APPEND)
 
+        {%- if set_interface_link_directories %}
+
+        # This is only used for '#pragma comment(lib, "foo")' (automatic link)
+        set_property(TARGET {{root_target_name}}
+                     PROPERTY INTERFACE_LINK_DIRECTORIES
+                     $<$<CONFIG:{{configuration}}>:${{'{'}}{{pkg_name}}_LIB_DIRS{{config_suffix}}}> APPEND)
+        {%- endif %}
         ########## COMPONENTS TARGET PROPERTIES {{ configuration }} ########################################
 
         {%- for comp_variable_name, comp_target_name in components_names %}
@@ -148,8 +159,15 @@ def template(self):
                      {{tvalue(pkg_name, comp_variable_name, 'COMPILE_OPTIONS_CXX', config_suffix)}}> APPEND)
         set({{ pkg_name }}_{{ comp_variable_name }}_TARGET_PROPERTIES TRUE)
 
+        {%- if set_interface_link_directories %}
+        # This is only used for '#pragma comment(lib, "foo")' (automatic link)
+        set_property(TARGET {{ comp_target_name }} PROPERTY INTERFACE_LINK_DIRECTORIES
+                     $<$<CONFIG:{{ configuration }}>:{{tvalue(pkg_name, comp_variable_name, 'LIB_DIRS', config_suffix)}}> APPEND)
+
+        {%- endif %}
         {%- endfor %}
 
+
         """)
 
     def get_declared_components_targets_names(self):


Test patch:
diff --git a/conans/test/functional/toolchains/cmake/cmakedeps/test_link_order.py b/conans/test/functional/toolchains/cmake/cmakedeps/test_link_order.py
--- a/conans/test/functional/toolchains/cmake/cmakedeps/test_link_order.py
+++ b/conans/test/functional/toolchains/cmake/cmakedeps/test_link_order.py
@@ -226,7 +226,9 @@ def _get_link_order_from_cmake(content):
         if 'main.cpp.o -o example' in line:
             _, links = line.split("main.cpp.o -o example")
             for it_lib in links.split():
-                if it_lib.startswith("-l"):
+                if it_lib.startswith("-L") or it_lib.startswith("-Wl,-rpath"):
+                    continue
+                elif it_lib.startswith("-l"):
                     libs.append(it_lib[2:])
                 elif it_lib == "-framework":
                     continue
@@ -258,6 +260,9 @@ def _get_link_order_from_xcode(content):
     start_key = '-headerpad_max_install_names",'
     end_key = ');'
     libs_content = content.split(start_key, 1)[1].split(end_key, 1)[0]
+    if libs_content == '"$(inherited)"':
+        # FIXME: Dirty hack, sometimes the library list is not at the 1 but at 2
+        libs_content = content.split(start_key, 1)[2].split(end_key, 1)[0]
     libs_unstripped = libs_content.split(",")
     for lib in libs_unstripped:
         if ".a" in lib:
diff --git a/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py b/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
--- a/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
+++ b/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
@@ -5,7 +5,6 @@
 import pytest
 
 from conan.tools.cmake.presets import load_cmake_presets
-from conan.tools.files.files import load_toolchain_args
 from conans.model.ref import ConanFileReference
 from conans.test.assets.cmake import gen_cmakelists
 from conans.test.assets.genconanfile import GenConanfile
@@ -228,3 +227,101 @@ def test_install_output_directories():
     layout_folder = "cmake-build-release" if platform.system() != "Windows" else "build"
     toolchain = client.load(os.path.join(b_folder, layout_folder, "conan", "conan_toolchain.cmake"))
     assert 'set(CMAKE_INSTALL_LIBDIR "mylibs")' in toolchain
+
+
+class TestAutoLinkPragma:
+
+    # Consumer test_package setting cmake_deps.set_interface_link_directories = True
+    test_cf = textwrap.dedent("""
+        import os
+
+        from conan import ConanFile
+        from conan.tools.cmake import CMake, cmake_layout, CMakeDeps
+        from conan.tools.build import cross_building
+
+
+        class HelloTestConan(ConanFile):
+            settings = "os", "compiler", "build_type", "arch"
+            generators = "CMakeToolchain", "VirtualBuildEnv", "VirtualRunEnv"
+            apply_env = False
+            test_type = "explicit"
+
+            def generate(self):
+                deps = CMakeDeps(self)
+                deps.set_interface_link_directories = True
+                deps.generate()
+
+            def requirements(self):
+                self.requires(self.tested_reference_str)
+
+            def build(self):
+                cmake = CMake(self)
+                cmake.configure()
+                cmake.build()
+
+            def layout(self):
+                cmake_layout(self)
+
+            def test(self):
+                if not cross_building(self):
+                    cmd = os.path.join(self.cpp.build.bindirs[0], "example")
+                    self.run(cmd, env="conanrun")
+        """)
+
+    @pytest.mark.skipif(platform.system() != "Windows", reason="Requires Visual Studio")
+    @pytest.mark.tool_cmake
+    def test_autolink_pragma_components(self):
+        """https://github.com/conan-io/conan/issues/10837
+
+        NOTE: At the moment the property cmake_set_interface_link_directories is only read at the
+        global cppinfo, not in the components"""
+
+        client = TestClient()
+        client.run("new hello/1.0 --template cmake_lib")
+        cf = client.load("conanfile.py")
+        cf = cf.replace('self.cpp_info.libs = ["hello"]', """
+            self.cpp_info.components['my_component'].includedirs.append('include')
+            self.cpp_info.components['my_component'].libdirs.append('lib')
+            self.cpp_info.components['my_component'].libs = []
+            self.cpp_info.set_property("cmake_set_interface_link_directories", True)
+        """)
+        hello_h = client.load("include/hello.h")
+        hello_h = hello_h.replace("#define hello_EXPORT __declspec(dllexport)",
+                                  '#define hello_EXPORT __declspec(dllexport)\n'
+                                  '#pragma comment(lib, "hello")')
+
+        test_cmakelist = client.load("test_package/CMakeLists.txt")
+        test_cmakelist = test_cmakelist.replace("target_link_libraries(example hello::hello)",
+                                                "target_link_libraries(example hello::my_component)")
+        client.save({"conanfile.py": cf,
+                     "include/hello.h": hello_h,
+                     "test_package/CMakeLists.txt": test_cmakelist,
+                     "test_package/conanfile.py": self.test_cf})
+
+        client.run("create .")
+
+
+    @pytest.mark.skipif(platform.system() != "Windows", reason="Requires Visual Studio")
+    @pytest.mark.tool_cmake
+    def test_autolink_pragma_without_components(self):
+        """https://github.com/conan-io/conan/issues/10837"""
+        client = TestClient()
+        client.run("new hello/1.0 --template cmake_lib")
+        cf = client.load("conanfile.py")
+        cf = cf.replace('self.cpp_info.libs = ["hello"]', """
+            self.cpp_info.includedirs.append('include')
+            self.cpp_info.libdirs.append('lib')
+            self.cpp_info.libs = []
+            self.cpp_info.set_property("cmake_set_interface_link_directories", True)
+        """)
+        hello_h = client.load("include/hello.h")
+        hello_h = hello_h.replace("#define hello_EXPORT __declspec(dllexport)",
+                                  '#define hello_EXPORT __declspec(dllexport)\n'
+                                  '#pragma comment(lib, "hello")')
+
+        client.save({"conanfile.py": cf,
+                     "include/hello.h": hello_h,
+                     "test_package/conanfile.py": self.test_cf})
+
+        client.run("create .")
+
diff --git a/conans/test/unittests/tools/cmake/test_cmakedeps.py b/conans/test/unittests/tools/cmake/test_cmakedeps.py
--- a/conans/test/unittests/tools/cmake/test_cmakedeps.py
+++ b/conans/test/unittests/tools/cmake/test_cmakedeps.py
@@ -31,6 +31,7 @@ def test_cpp_info_name_cmakedeps():
     conanfile_dep._conan_node = Mock()
     conanfile_dep._conan_node.ref = ConanFileReference.loads("OriginalDepName/1.0")
     conanfile_dep._conan_node.context = "host"
+    conanfile_dep.settings = conanfile.settings
     conanfile_dep.folders.set_base_package("/path/to/folder_dep")
 
     with mock.patch('conans.ConanFile.dependencies', new_callable=mock.PropertyMock) as mock_deps:
@@ -64,6 +65,7 @@ def test_cpp_info_name_cmakedeps_components():
 
     conanfile_dep = ConanFile(Mock(), None)
     conanfile_dep.cpp_info = cpp_info
+    conanfile_dep.settings = conanfile.settings
     conanfile_dep._conan_node = Mock()
     conanfile_dep._conan_node.ref = ConanFileReference.loads("OriginalDepName/1.0")
     conanfile_dep._conan_node.context = "host"
@@ -104,6 +106,7 @@ def test_cmake_deps_links_flags():
     cpp_info.objects = ["myobject.o"]
     conanfile_dep = ConanFile(Mock(), None)
     conanfile_dep.cpp_info = cpp_info
+    conanfile_dep.settings = conanfile.settings
     conanfile_dep._conan_node = Mock()
     conanfile_dep._conan_node.ref = ConanFileReference.loads("mypkg/1.0")
     conanfile_dep._conan_node.context = "host"
@@ -145,6 +148,7 @@ def test_component_name_same_package():
 
     conanfile_dep = ConanFile(Mock(), None)
     conanfile_dep.cpp_info = cpp_info
+    conanfile_dep.settings = conanfile.settings
     conanfile_dep._conan_node = Mock()
     conanfile_dep._conan_node.context = "host"
     conanfile_dep._conan_node.ref = ConanFileReference.loads("mypkg/1.0")