Repository: conan-io/conan

Issue:
[build system] [MSBuild] msbuild: generate props file with dependent libraries list for linking
conan-1.4.5

If I understand correctly Conan now passes LIB environment variable as dependent libraries search path, and generates props file include directories and other compiler flags. (btw documentation is totally unclear about this, so I had to dig to the Conan sources a lot)
What Conan doesn't do is generation of a list of libraries to link with. So it's supposed Visual Studio project file already has valid names of libraries what unfortunately is not always true.

What I suggest is to add an option to generate the libraries list as well. So a maintainer of a package can use something like replace_in_file to remove known invalid libraries names from a project file and let Conan do its business with linking.

Usually it's not an issue when VS files are also maintained by the same maintainer, otherwise it is.


Discussion:
This is the extract of the VS generator:
```xml
<Link>
      <AdditionalLibraryDirectories>{lib_dirs}%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
      <AdditionalDependencies>{libs}%(AdditionalDependencies)</AdditionalDependencies>
      <AdditionalOptions>{linker_flags} %(AdditionalOptions)</AdditionalOptions>
    </Link>
```

I think the problem is that Visual Studio doesn't use other environment variables rather than LIB and /I, so defining other environment variables have no effect:

https://msdn.microsoft.com/en-us/library/19z1t1wy.aspx
https://msdn.microsoft.com/en-us/library/fwkeyyhe.aspx
https://msdn.microsoft.com/en-us/library/9s7c9wdw.aspx

So the only way to achieve this would be to inject the generated file somehow. It might make sense to provide some functionality to simplify this, and make it possible to make it programmatically, and not via de IDE
msbuild already has code generating props file which is injected automatically https://github.com/conan-io/conan/blob/develop/conans/client/build/msbuild.py#L104

But currently there is nothing about dependent libraries.
> But currently there is nothing about dependent libraries.

There are 2 different files. One of them, the one in https://github.com/conan-io/conan/blob/develop/conans/client/build/msbuild.py#L104, is used for the current project configuration, to make sure it is compatible with the given settings (basically setting the runtime).

All the dependencies information is set in the generated ``conanbuildinfo.props`` which is not passed in the same way. It is not only the LIBS, but also the include paths, library paths, definitions needed to set... all of them are in the ``conanbuildinfo.props``, so it wouldn't be enough to set the LIBS in that other temporary .props file, that is injected dynamically. 

I agree that this need some improvements, and not only this, but also other generators/build-helpers need a re-thinking how conan integrates with them. This is the reason I renamed this issue as ``[build system]``, to easily classify them and propose a new approach that is aligned with other build systems too (and not breaking, unless until conan 2.0)

Resolving patch:
diff --git a/conans/client/build/visual_environment.py b/conans/client/build/visual_environment.py
--- a/conans/client/build/visual_environment.py
+++ b/conans/client/build/visual_environment.py
@@ -10,10 +10,12 @@ class VisualStudioBuildEnvironment(object):
     """
     - LIB: library paths with semicolon separator
     - CL: /I (include paths)
+    - _LINK_: linker options and libraries
 
     https://msdn.microsoft.com/en-us/library/19z1t1wy.aspx
     https://msdn.microsoft.com/en-us/library/fwkeyyhe.aspx
     https://msdn.microsoft.com/en-us/library/9s7c9wdw.aspx
+    https://msdn.microsoft.com/en-us/library/6y6t9esh.aspx
 
     """
     def __init__(self, conanfile):
@@ -32,6 +34,7 @@ def __init__(self, conanfile):
         self.flags = self._configure_flags()
         self.cxx_flags = copy.copy(self._deps_cpp_info.cppflags)
         self.link_flags = self._configure_link_flags()
+        self.libs = conanfile.deps_cpp_info.libs
         self.std = self._std_cpp()
         self.parallel = False
 
@@ -60,7 +63,6 @@ def _get_cl_list(self, quotes=True):
         ret.extend(format_defines(self.defines))
         ret.extend(self.flags)
         ret.extend(self.cxx_flags)
-        ret.extend(self.link_flags)
 
         if self.parallel:  # Build source in parallel
             ret.append(parallel_compiler_cl_flag())
@@ -70,20 +72,34 @@ def _get_cl_list(self, quotes=True):
 
         return ret
 
+    def _get_link_list(self):
+        def format_lib(lib):
+            return lib if lib.endswith('.lib') else '%s.lib' % lib
+
+        ret = [flag for flag in self.link_flags]  # copy
+        ret.extend([format_lib(lib) for lib in self.libs])
+
+        return ret
+
     @property
     def vars(self):
         """Used in conanfile with environment_append"""
         flags = self._get_cl_list()
+        link_flags = self._get_link_list()
+
         cl_args = " ".join(flags) + _environ_value_prefix("CL")
+        link_args = " ".join(link_flags)
         lib_paths = ";".join(['%s' % lib for lib in self.lib_paths]) + _environ_value_prefix("LIB", ";")
         return {"CL": cl_args,
-                "LIB": lib_paths}
+                "LIB": lib_paths,
+                "_LINK_": link_args}
 
     @property
     def vars_dict(self):
         """Used in virtualbuildenvironment"""
         # Here we do not quote the include paths, it's going to be used by virtual environment
         cl = self._get_cl_list(quotes=False)
+        link = self._get_link_list()
 
         lib = [lib for lib in self.lib_paths]  # copy
 
@@ -93,8 +109,12 @@ def vars_dict(self):
         if os.environ.get("LIB", None):
             lib.append(os.environ.get("LIB"))
 
+        if os.environ.get("_LINK_", None):
+            link.append(os.environ.get("_LINK_"))
+
         ret = {"CL": cl,
-               "LIB": lib}
+               "LIB": lib,
+               "_LINK_": link}
         return ret
 
     def _std_cpp(self):


Test patch:
diff --git a/conans/test/build_helpers/visual_environment_test.py b/conans/test/build_helpers/visual_environment_test.py
--- a/conans/test/build_helpers/visual_environment_test.py
+++ b/conans/test/build_helpers/visual_environment_test.py
@@ -23,6 +23,7 @@ def test_visual(self):
         conanfile.deps_cpp_info.cppflags.append("-mycppflag2")
         conanfile.deps_cpp_info.exelinkflags.append("-myexelinkflag")
         conanfile.deps_cpp_info.sharedlinkflags.append("-mysharedlinkflag")
+        conanfile.deps_cpp_info.libs.extend(['gdi32', 'user32.lib'])
 
         tool = VisualStudioBuildEnvironment(conanfile)
         self.assertEquals(tool.vars_dict, {
@@ -34,10 +35,9 @@ def test_visual(self):
                    '-Ob0',
                    '-Od',
                    '-mycppflag',
-                   '-mycppflag2',
-                   '-myexelinkflag',
-                   '-mysharedlinkflag'],
+                   '-mycppflag2'],
             "LIB": ["/one/lib/path", "/two/lib/path"],
+            "_LINK_": ['-myexelinkflag', '-mysharedlinkflag', 'gdi32.lib', 'user32.lib']
         })
         tool.parallel = True
         self.assertEquals(tool.vars_dict, {
@@ -50,10 +50,9 @@ def test_visual(self):
                    '-Od',
                    '-mycppflag',
                    '-mycppflag2',
-                   '-myexelinkflag',
-                   '-mysharedlinkflag',
                    '/MP%s' % tools.cpu_count()],
             "LIB": ["/one/lib/path", "/two/lib/path"],
+            "_LINK_": ['-myexelinkflag', '-mysharedlinkflag', 'gdi32.lib', 'user32.lib']
         })
         tool.parallel = False
 
@@ -72,10 +71,9 @@ def test_visual(self):
                    '-Ob0',
                    '-Od',
                    '-mycppflag',
-                   '-mycppflag2',
-                   '-myexelinkflag',
-                   '-mysharedlinkflag'],
+                   '-mycppflag2'],
             "LIB": ["/one/lib/path", "/two/lib/path", "/three/lib/path"],
+            "_LINK_": ['-myexelinkflag', '-mysharedlinkflag', 'gdi32.lib', 'user32.lib']
         })
 
         # Now try appending to environment
@@ -92,18 +90,18 @@ def test_visual(self):
                        '-Od',
                        '-mycppflag',
                        '-mycppflag2',
-                       '-myexelinkflag',
-                       '-mysharedlinkflag',
                        "-I/four/include/path -I/five/include/path"],
                 "LIB": ["/one/lib/path", "/two/lib/path", "/three/lib/path", "/four/lib/path;/five/lib/path"],
+                "_LINK_": ['-myexelinkflag', '-mysharedlinkflag', 'gdi32.lib', 'user32.lib']
             })
 
             self.assertEquals(tool.vars, {
                 "CL": '-I"/one/include/path" -I"/two/include/path" -I"/three/include/path" -MDd '
                       '-mycflag -mycflag2 -Zi -Ob0 -Od '
-                      '-mycppflag -mycppflag2 -myexelinkflag -mysharedlinkflag '
+                      '-mycppflag -mycppflag2 '
                       '-I/four/include/path -I/five/include/path',
                 "LIB": "/one/lib/path;/two/lib/path;/three/lib/path;/four/lib/path;/five/lib/path",
+                "_LINK_": "-myexelinkflag -mysharedlinkflag gdi32.lib user32.lib"
             })
 
     def build_type_toolset_test(self):