Repository: conan-io/conan

Issue:
Virtualbuildenv generator should setup Visual Studio paths
I believe Virtualbuildenv generator should generate `activate_build.bat` that setup the whole Visual Studio environment like `tools.vcvars()` does.


Discussion:

Resolving patch:
diff --git a/conans/client/generators/virtualbuildenv.py b/conans/client/generators/virtualbuildenv.py
--- a/conans/client/generators/virtualbuildenv.py
+++ b/conans/client/generators/virtualbuildenv.py
@@ -1,6 +1,7 @@
 from conans.client.generators.virtualenv import VirtualEnvGenerator
 from conans.client.build.autotools_environment import AutoToolsBuildEnvironment
 from conans.client.build.visual_environment import VisualStudioBuildEnvironment
+from conans.tools import vcvars_dict
 
 
 class VirtualBuildEnvGenerator(VirtualEnvGenerator):
@@ -9,12 +10,11 @@ def __init__(self, conanfile):
         super(VirtualBuildEnvGenerator, self).__init__(conanfile)
         self.venv_name = "conanbuildenv"
         compiler = conanfile.settings.get_safe("compiler")
-        if compiler != "Visual Studio":
-            tools = AutoToolsBuildEnvironment(conanfile)
+        if compiler == "Visual Studio":
+            self.env = VisualStudioBuildEnvironment(conanfile).vars_dict
+            self.env.update(vcvars_dict(conanfile.settings))
         else:
-            tools = VisualStudioBuildEnvironment(conanfile)
-
-        self.env = tools.vars_dict
+            self.env = AutoToolsBuildEnvironment(conanfile).vars_dict
 
     @property
     def content(self):
diff --git a/conans/client/generators/virtualenv.py b/conans/client/generators/virtualenv.py
--- a/conans/client/generators/virtualenv.py
+++ b/conans/client/generators/virtualenv.py
@@ -71,7 +71,8 @@ def append_deactivate_lines(var_names):
                 if platform.system() == "Windows":
                     ret.append('SET %s=%s' % (name, old_value))
                 else:
-                    ret.append('export %s=%s' % (name, old_value))
+                    line = "export %s=%s" % (name, old_value) if old_value else ("unset %s" % name)
+                    ret.append(line)
 
             if platform.system() == "Windows":
                 ret.append("SET PROMPT=%s" % os.environ.get("PROMPT", ""))
diff --git a/conans/client/tools/win.py b/conans/client/tools/win.py
--- a/conans/client/tools/win.py
+++ b/conans/client/tools/win.py
@@ -298,7 +298,7 @@ def vcvars_command(settings, arch=None, compiler_version=None, force=False, vcva
 
 
 def vcvars_dict(settings, arch=None, compiler_version=None, force=False, filter_known_paths=False,
-                only_diff=True, vcvars_ver=None, winsdk_version=None):
+                vcvars_ver=None, winsdk_version=None, only_diff=True):
 
     cmd = vcvars_command(settings, arch=arch,
                          compiler_version=compiler_version, force=force,


Test patch:
diff --git a/conans/test/generators/virtualbuildenv_test.py b/conans/test/generators/virtualbuildenv_test.py
new file mode 100644
--- /dev/null
+++ b/conans/test/generators/virtualbuildenv_test.py
@@ -0,0 +1,60 @@
+import os
+import platform
+import subprocess
+import unittest
+
+from conans import load
+from conans.test.utils.tools import TestClient
+
+
+class VirtualBuildEnvTest(unittest.TestCase):
+
+    def environment_deactivate_test(self):
+
+        in_windows = platform.system() == "Windows"
+        env_cmd = "set" if in_windows else "env"
+        extension = "bat" if in_windows else "sh"
+
+        def env_output_to_dict(env_output):
+            env = {}
+            for line in env_output.splitlines():
+                tmp = line.decode().split("=")
+                if tmp[0] not in ["SHLVL", "_", "PS1"]:
+                    env[tmp[0]] = tmp[1].replace("\\", "/")
+            return env
+
+        def get_cmd(script_name):
+            if in_windows:
+                return "%s && set" % script_name
+            else:
+                return "bash -c 'source %s && env'" % script_name
+
+        conanfile = """
+from conans import ConanFile
+
+class TestConan(ConanFile):
+    name = "test"
+    version = "1.0"
+    settings = "os", "compiler", "arch", "build_type"
+    generators = "virtualbuildenv"
+"""
+        client = TestClient(path_with_spaces=False)
+        client.save({"conanfile.py": conanfile})
+        client.run("install .")
+        output = subprocess.check_output(env_cmd, shell=True)
+        normal_environment = env_output_to_dict(output)
+        client.run("install .")
+        act_build_file = os.path.join(client.current_folder, "activate_build.%s" % extension)
+        deact_build_file = os.path.join(client.current_folder, "deactivate_build.%s" % extension)
+        self.assertTrue(os.path.exists(act_build_file))
+        self.assertTrue(os.path.exists(deact_build_file))
+        if in_windows:
+            act_build_content_len = len(load(act_build_file).splitlines())
+            deact_build_content_len = len(load(deact_build_file).splitlines())
+            self.assertEqual(act_build_content_len, deact_build_content_len)
+        output = subprocess.check_output(get_cmd(act_build_file), shell=True)
+        activate_environment = env_output_to_dict(output)
+        self.assertNotEqual(normal_environment, activate_environment)
+        output = subprocess.check_output(get_cmd(deact_build_file), shell=True)
+        deactivate_environment = env_output_to_dict(output)
+        self.assertEqual(normal_environment, deactivate_environment)
diff --git a/conans/test/integration/conan_env_test.py b/conans/test/integration/conan_env_test.py
--- a/conans/test/integration/conan_env_test.py
+++ b/conans/test/integration/conan_env_test.py
@@ -445,9 +445,12 @@ def package_info(self):
             self.assertIn("PATH=/dir", activate_contents)
         else:
             self.assertIn("PATH=\"/dir\"", activate_contents)
-
-        self.assertIn('var1=', deactivate_contents)
-        self.assertIn('var2=', deactivate_contents)
+        if platform.system() == "Windows":
+            self.assertIn('var1=', deactivate_contents)
+            self.assertIn('var2=', deactivate_contents)
+        else:
+            self.assertIn('unset var1', deactivate_contents)
+            self.assertIn('unset var2', deactivate_contents)
 
     def test_conan_info_cache_and_priority(self):
         client = TestClient()