Repository: conan-io/conan

Issue:
Visual Studio Generator: PATH variable not correct
Hi

I am using the visual studio generator to generate a 'conanbuildinfo.props' file. The PATH variable in props file has the wrong directory separator char. In Windows CMD paths with '/' aren't supported in the PATH variable. Since the PATH variable isnt correct, the executables aren't be found if i am using ShellExecute in the code. 

An example of a generated props file is:
`<LocalDebuggerEnvironment>PATH=%PATH%;$(USERPROFILE)/.conan/data/xx/0.6.0/3rdparty/release/package/c85f9b4041181d7a/bin;</LocalDebuggerEnvironment>  
    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>`

It is supposed to looks like:
`<LocalDebuggerEnvironment>PATH=%PATH%;$(USERPROFILE)\.conan\data\xx\0.6.0\3rdparty\release\package\c85f9b4041181d7a\bin;</LocalDebuggerEnvironment>  
    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>`

Version info:
Conan 1.14
Visual Studio 2015
Windows 10 Pro

The issue is in the file: conans/client/generators/visualstudio.py:
Line 25: <LocalDebuggerEnvironment>PATH=%PATH%;{bin_dirs}</LocalDebuggerEnvironment>
Line 70: 'bin_dirs': "".join("%s;" % p for p in build_info.bin_paths).replace("\\", "/"),'

To help us debug your issue please explain:

- [x] I've read the [CONTRIBUTING guide](https://github.com/conan-io/conan/blob/develop/.github/CONTRIBUTING.md).
- [x] I've specified the Conan version, operating system version and any tool that can be relevant.
- [x] I've explained the steps to reproduce the error or the motivation/use case of the question/suggestion.




Discussion:
Thanks for reporting. We will try to solve it for 1.15.
Hi again, I tried to do a simple test setting PATH in the cmd to a folder with "/" instead of "\" and it works. Is it not working for you? It is something related with VS maybe?
Hi
I did the same test with the path variable in the cmd promt and it worked.
So I created a small test project to demonstrate the issue. In the attached zip file are two folders. One is a simple binary package with the windows write program. The second one is the visual studio 2015 solution with a simple mfc dialog. On clicking the OK button the windows write program (included in the conan wite package) should open.

If the path in the props file contains '/' it is not working for me. If I changed the '/' to '\\' in the props file (line 13) it works for me.

[test.zip](https://github.com/conan-io/conan/files/3089565/test.zip)


Resolving patch:
diff --git a/conans/client/generators/visualstudio.py b/conans/client/generators/visualstudio.py
--- a/conans/client/generators/visualstudio.py
+++ b/conans/client/generators/visualstudio.py
@@ -53,7 +53,7 @@ def _format_items(self):
         sections = []
         for dep_name, cpp_info in self._deps_build_info.dependencies:
             fields = {
-                'root_dir': cpp_info.rootpath.replace("\\", "/"),
+                'root_dir': cpp_info.rootpath,
                 'name': dep_name.replace(".", "-")
             }
             section = self.item_template.format(**fields)
@@ -67,10 +67,10 @@ def filename(self):
     def _format_properties(self, build_info, condition):
         fields = {
             'condition': condition,
-            'bin_dirs': "".join("%s;" % p for p in build_info.bin_paths).replace("\\", "/"),
-            'res_dirs': "".join("%s;" % p for p in build_info.res_paths).replace("\\", "/"),
-            'include_dirs': "".join("%s;" % p for p in build_info.include_paths).replace("\\", "/"),
-            'lib_dirs': "".join("%s;" % p for p in build_info.lib_paths).replace("\\", "/"),
+            'bin_dirs': "".join("%s;" % p for p in build_info.bin_paths),
+            'res_dirs': "".join("%s;" % p for p in build_info.res_paths),
+            'include_dirs': "".join("%s;" % p for p in build_info.include_paths),
+            'lib_dirs': "".join("%s;" % p for p in build_info.lib_paths),
             'libs': "".join(['%s.lib;' % lib if not lib.endswith(".lib")
                              else '%s;' % lib for lib in build_info.libs]),
             'definitions': "".join("%s;" % d for d in build_info.defines),
@@ -98,7 +98,7 @@ def content(self):
 
         userprofile = os.getenv("USERPROFILE")
         if userprofile:
-            userprofile = userprofile.replace("\\", "/")
+            userprofile = userprofile.replace("\\", "\\\\")
             formatted_template = re.sub(userprofile, "$(USERPROFILE)", formatted_template,
                                         flags=re.I)
         return formatted_template


Test patch:
diff --git a/conans/test/functional/generators/xcode_gcc_vs_test.py b/conans/test/functional/generators/xcode_gcc_vs_test.py
--- a/conans/test/functional/generators/xcode_gcc_vs_test.py
+++ b/conans/test/functional/generators/xcode_gcc_vs_test.py
@@ -68,7 +68,8 @@ def package_info(self):
         definition_group = xmldoc.getElementsByTagName('ItemDefinitionGroup')[0]
         compiler = definition_group.getElementsByTagName("ClCompile")[0]
 
-        include_dirs = compiler.getElementsByTagName("AdditionalIncludeDirectories")[0].firstChild.data
+        elem = compiler.getElementsByTagName("AdditionalIncludeDirectories")
+        include_dirs = elem[0].firstChild.data
         definitions = compiler.getElementsByTagName("PreprocessorDefinitions")[0].firstChild.data
 
         linker = definition_group.getElementsByTagName("Link")[0]
@@ -77,12 +78,12 @@ def package_info(self):
 
         package_id = os.listdir(client.cache.packages(ref))[0]
         pref = PackageReference(ref, package_id)
-        package_path = client.cache.package(pref).replace("\\", "/")
+        package_path = client.cache.package(pref)
 
-        replaced_path = re.sub(os.getenv("USERPROFILE", "not user profile").replace("\\", "/"),
+        replaced_path = re.sub(os.getenv("USERPROFILE", "not user profile").replace("\\", "\\\\"),
                                "$(USERPROFILE)", package_path, flags=re.I)
-        expected_lib_dirs = os.path.join(replaced_path, "lib").replace("\\", "/")
-        expected_include_dirs = os.path.join(replaced_path, "include").replace("\\", "/")
+        expected_lib_dirs = os.path.join(replaced_path, "lib")
+        expected_include_dirs = os.path.join(replaced_path, "include")
 
         self.assertIn(expected_lib_dirs, lib_dirs)
         self.assertEqual("hello.lib;%(AdditionalDependencies)", libs)
@@ -102,4 +103,5 @@ def package_info(self):
         self.assertIn("GCC_PREPROCESSOR_DEFINITIONS = $(inherited)", xcode)
         self.assertIn('OTHER_CFLAGS = $(inherited) %s' % expected_c_flags, xcode)
         self.assertIn('OTHER_CPLUSPLUSFLAGS = $(inherited) %s' % expected_cpp_flags, xcode)
-        self.assertIn('FRAMEWORK_SEARCH_PATHS = $(inherited) "%s"' % package_path.replace("\\", "/"), xcode)
+        self.assertIn('FRAMEWORK_SEARCH_PATHS = $(inherited) "%s"' % package_path.replace("\\", "/"),
+                      xcode)
diff --git a/conans/test/unittests/client/generators/visual_studio_test.py b/conans/test/unittests/client/generators/visual_studio_test.py
--- a/conans/test/unittests/client/generators/visual_studio_test.py
+++ b/conans/test/unittests/client/generators/visual_studio_test.py
@@ -43,27 +43,28 @@ def user_profile_test(self):
         pkg1 = os.path.join(tmp_folder, "pkg1")
         cpp_info = CppInfo(pkg1)
         cpp_info.includedirs = ["include"]
-        save(os.path.join(pkg1, "include/file.h"), "")
+        save(os.path.join(pkg1, "include", "file.h"), "")
         conanfile.deps_cpp_info.update(cpp_info, ref.name)
         ref = ConanFileReference.loads("My.Fancy-Pkg_2/0.1@user/testing")
         pkg2 = os.path.join(tmp_folder, "pkg2")
         cpp_info = CppInfo(pkg2)
         cpp_info.includedirs = ["include"]
-        save(os.path.join(pkg2, "include/file.h"), "")
+        save(os.path.join(pkg2, "include", "file.h"), "")
         conanfile.deps_cpp_info.update(cpp_info, ref.name)
         generator = VisualStudioGenerator(conanfile)
 
+        path1 = os.path.join("$(USERPROFILE)", "pkg1", "include")
+        path2 = os.path.join("$(USERPROFILE)", "pkg2", "include")
+        expected = "<AdditionalIncludeDirectories>%s;%s;" % (path1, path2)
         with tools.environment_append({"USERPROFILE": tmp_folder}):
             content = generator.content
             xml.etree.ElementTree.fromstring(content)
-            self.assertIn("<AdditionalIncludeDirectories>$(USERPROFILE)/pkg1/include;"
-                          "$(USERPROFILE)/pkg2/include;", content)
+            self.assertIn(expected, content)
 
         with tools.environment_append({"USERPROFILE": tmp_folder.upper()}):
             content = generator.content
             xml.etree.ElementTree.fromstring(content)
-            self.assertIn("<AdditionalIncludeDirectories>$(USERPROFILE)/pkg1/include;"
-                          "$(USERPROFILE)/pkg2/include;", content)
+            self.assertIn(expected, content)
 
     def multi_config_test(self):
         tmp_folder = temp_folder()
@@ -87,11 +88,11 @@ def multi_config_test(self):
                         "_DEBUG;DEBUG;%(PreprocessorDefinitions)" \
                         "</PreprocessorDefinitions>"
         defines_release = "<PreprocessorDefinitions>" \
-                        "NDEBUG;%(PreprocessorDefinitions)" \
-                        "</PreprocessorDefinitions>"
-        defines_custom = "<PreprocessorDefinitions>" \
-                          "CUSTOM_BUILD;%(PreprocessorDefinitions)" \
+                          "NDEBUG;%(PreprocessorDefinitions)" \
                           "</PreprocessorDefinitions>"
+        defines_custom = "<PreprocessorDefinitions>" \
+                         "CUSTOM_BUILD;%(PreprocessorDefinitions)" \
+                         "</PreprocessorDefinitions>"
         self.assertIn(defines_common, content)
         self.assertIn(defines_debug, content)
         self.assertIn(defines_release, content)