Repository: conan-io/conan

Issue:
AutoToolsBuildEnvironment improvements
1. Should AutoToolsBuildEnvironment.configure adjust --prefix to self.package_folder?

The same way we are doing it with CMake.
Be aware: Could be breaking, opt-in?

2. The "vars" should be a real list (not a dynamic property) precalculated to let the user change it before calling the configure
3. The triplets for cross-building should be also pre-calculated and we should allow to change them. Probably to a "args" list.

In general, if someone doesn't want to use the ".configure()":

```python
   env_build = AutoToolsBuildEnvironment(self)
   # Here I modify the env_build.vars
   # Here I modify the env_buidl.args
   with  #tools.environment_append(env_build.vars):
         self.run("./myconfigure %s" % env_build.args)
```

Note: Inspired by the libcurl recipe, where the above issues make a lot more complex the recipe.


Discussion:
Note: the issue is that we need 2 steps, first being able to change the flags that will be set in the env vars and a second one where we can alter the env vars (and arguments).
Note2: The `--prefix=` has to be without quotes or it appears to break always.
Use `"--prefix=%s" % self.package_folder` directly

Resolving patch:
diff --git a/conans/client/build/autotools_environment.py b/conans/client/build/autotools_environment.py
--- a/conans/client/build/autotools_environment.py
+++ b/conans/client/build/autotools_environment.py
@@ -12,7 +12,7 @@
 from conans.client.tools.oss import OSInfo
 from conans.client.tools.win import unix_path
 from conans.tools import (environment_append, args_to_string, cpu_count, cross_building,
-                          detected_architecture)
+                          detected_architecture, get_gnu_triplet)
 
 
 class AutoToolsBuildEnvironment(object):
@@ -57,68 +57,32 @@ def __init__(self, conanfile, win_bash=False, include_rpath_flags=False):
         # Not declared by default
         self.fpic = None
 
-    def _get_triplet(self, the_arch, the_os):
-        """
-        machine-vendor-op_system, But vendor can be omitted in practice
-        """
-
-        # Calculate the arch
-        machine = {"x86": "i686" if the_os != "Linux" else "x86",
-                   "x86_64": "x86_64",
-                   "armv6": "arm",
-                   "armv7": "arm",
-                   "armv7s": "arm",
-                   "armv7k": "arm",
-                   "armv7hf": "arm",
-                   "armv8": "aarch64"}.get(the_arch, None)
-        if machine is None:
-            self._conanfile.output.warn("Unknown '%s' machine, Conan doesn't know how to "
-                                        "translate it to the GNU triplet, please report at "
-                                        " https://github.com/conan-io/conan/issues" % the_arch)
-            return "unknown"
-
-        # Calculate the OS
-        compiler = self._conanfile.settings.get_safe("compiler")
-        if compiler == "gcc":
-            windows_op = "w64-mingw32"
-        elif compiler == "Visual Studio":
-            windows_op = "windows-msvc"
-        else:
-            windows_op = "windows"
-
-        op_system = {"Windows": windows_op,
-                     "Linux": "linux-gnu",
-                     "Darwin": "apple-darwin",
-                     "Android": "linux-android",
-                     "Macos": "apple-darwin",
-                     "iOS": "apple-darwin",
-                     "watchOS": "apple-darwin",
-                     "tvOS": "apple-darwin"}.get(the_os, the_os.lower())
-
-        if the_os in ("Linux", "Android"):
-            if "arm" in the_arch and the_arch != "armv8":
-                op_system += "eabi"
-
-            if the_arch == "armv7hf" and the_os == "Linux":
-                op_system += "hf"
-
-        return "%s-%s" % (machine, op_system)
+        # Precalculate build, host, target triplets
+        self.build, self.host, self.target = self._get_host_build_target_flags()
 
-    def _get_host_build_target_flags(self, arch_detected, os_detected):
+    def _get_host_build_target_flags(self):
         """Based on google search for build/host triplets, it could need a lot
         and complex verification"""
 
+        arch_detected = detected_architecture() or platform.machine()
+        os_detected = platform.system()
+        arch_settings = self._conanfile.settings.get_safe("arch")
+        os_settings = self._conanfile.settings.get_safe("os")
+        compiler = self._conanfile.settings.get_safe("compiler")
+
+        if (os_detected is None or arch_detected is None or arch_settings is None or
+                os_settings is None):
+            return False, False, False
         if not cross_building(self._conanfile.settings, os_detected, arch_detected):
             return False, False, False
 
-        build = self._get_triplet(arch_detected, os_detected)
-        host = self._get_triplet(self._conanfile.settings.get_safe("arch"),
-                                 self._conanfile.settings.get_safe("os"))
+        build = get_gnu_triplet(os_detected, arch_detected, compiler)
+        host = get_gnu_triplet(os_settings, arch_settings, compiler)
 
         return build, host, None
 
     def configure(self, configure_dir=None, args=None, build=None, host=None, target=None,
-                  pkg_config_paths=None):
+                  pkg_config_paths=None, vars=None):
         """
         :param pkg_config_paths: Optional paths to locate the *.pc files
         :param configure_dir: Absolute or relative path to the configure script
@@ -141,25 +105,20 @@ def configure(self, configure_dir=None, args=None, build=None, host=None, target
             configure_dir = configure_dir.rstrip("/")
         else:
             configure_dir = "."
-        auto_build, auto_host, auto_target = None, None, None
-        if build is None or host is None or target is None:
-            arch_detected = detected_architecture() or platform.machine()
-            os_detected = platform.system()
-            flags = self._get_host_build_target_flags(arch_detected, os_detected)
-            auto_build, auto_host, auto_target = flags
+
         triplet_args = []
 
         if build is not False:  # Skipped by user
-            if build or auto_build:  # User specified value or automatic
-                triplet_args.append("--build=%s" % (build or auto_build))
+            if build or self.build:  # User specified value or automatic
+                triplet_args.append("--build=%s" % (build or self.build))
 
         if host is not False:   # Skipped by user
-            if host or auto_host:  # User specified value or automatic
-                triplet_args.append("--host=%s" % (host or auto_host))
+            if host or self.host:  # User specified value or automatic
+                triplet_args.append("--host=%s" % (host or self.host))
 
         if target is not False:  # Skipped by user
-            if target or auto_target:  # User specified value or automatic
-                triplet_args.append("--target=%s" % (target or auto_target))
+            if target or self.target:  # User specified value or automatic
+                triplet_args.append("--target=%s" % (target or self.target))
 
         if pkg_config_paths:
             pkg_env = {"PKG_CONFIG_PATH": os.pathsep.join(pkg_config_paths)}
@@ -169,8 +128,14 @@ def configure(self, configure_dir=None, args=None, build=None, host=None, target
             pkg_env = {"PKG_CONFIG_PATH": self._conanfile.build_folder} \
                 if "pkg_config" in self._conanfile.generators else {}
 
+        if self._conanfile.package_folder is not None:
+            if not args:
+                args = ["--prefix=%s" % self._conanfile.package_folder.replace("\\", "/")]
+            elif not any(["--prefix=" in arg for arg in args]):
+                args.append("--prefix=%s" % self._conanfile.package_folder.replace("\\", "/"))
+
         with environment_append(pkg_env):
-            with environment_append(self.vars):
+            with environment_append(vars or self.vars):
                 configure_dir = self._adjust_path(configure_dir)
                 command = '%s/configure %s %s' % (configure_dir,
                                                   args_to_string(args), " ".join(triplet_args))
@@ -184,17 +149,20 @@ def _adjust_path(self, path):
             path = unix_path(path, path_flavor=self.subsystem)
         return '"%s"' % path if " " in path else path
 
-    def make(self, args="", make_program=None, target=None):
+    def make(self, args="", make_program=None, target=None, vars=None):
         if not self._conanfile.should_build:
             return
         make_program = os.getenv("CONAN_MAKE_PROGRAM") or make_program or "make"
-        with environment_append(self.vars):
+        with environment_append(vars or self.vars):
             str_args = args_to_string(args)
             cpu_count_option = ("-j%s" % cpu_count()) if "-j" not in str_args else None
             self._conanfile.run("%s" % join_arguments([make_program, target, str_args,
                                                        cpu_count_option]),
                                 win_bash=self._win_bash, subsystem=self.subsystem)
 
+    def install(self, args="", make_program=None, vars=None):
+        self.make(args=args, make_program=make_program, target="install", vars=vars)
+
     def _configure_link_flags(self):
         """Not the -L"""
         ret = copy.copy(self._deps_cpp_info.sharedlinkflags)
@@ -313,7 +281,6 @@ def vars_dict(self):
 
     @property
     def vars(self):
-
         ld_flags, cpp_flags, libs, cxx_flags, c_flags = self._get_vars()
 
         cpp_flags = " ".join(cpp_flags) + _environ_value_prefix("CPPFLAGS")
@@ -328,7 +295,6 @@ def vars(self):
                "LDFLAGS": ldflags.strip(),
                "LIBS": libs.strip(),
                }
-
         return ret
 
 
diff --git a/conans/client/tools/oss.py b/conans/client/tools/oss.py
--- a/conans/client/tools/oss.py
+++ b/conans/client/tools/oss.py
@@ -307,6 +307,60 @@ def get_cross_building_settings(settings, self_os=None, self_arch=None):
     return build_os, build_arch, host_os, host_arch
 
 
+def get_gnu_triplet(os, arch, compiler=None):
+    """
+    Returns string with <machine>-<vendor>-<op_system> triplet (<vendor> can be omitted in practice)
+
+    :param os: os to be used to create the triplet
+    :param arch: arch to be used to create the triplet
+    :param compiler: compiler used to create the triplet (only needed fo windows)
+    """
+
+    if os == "Windows" and compiler is None:
+        raise ConanException("'compiler' parameter for 'get_gnu_triplet()' is not specified and "
+                             "needed for os=Windows")
+
+    # Calculate the arch
+    machine = {"x86": "i686" if os != "Linux" else "x86",
+                "x86_64": "x86_64",
+                "armv6": "arm",
+                "armv7": "arm",
+                "armv7s": "arm",
+                "armv7k": "arm",
+                "armv7hf": "arm",
+                "armv8": "aarch64"}.get(arch, None)
+    if machine is None:
+        raise ConanException("Unknown '%s' machine. Conan doesn't know how to "
+                                "translate it to the GNU triplet, please report at "
+                                "https://github.com/conan-io/conan/issues" % arch)
+
+    # Calculate the OS
+    if compiler == "gcc":
+        windows_op = "w64-mingw32"
+    elif compiler == "Visual Studio":
+        windows_op = "windows-msvc"
+    else:
+        windows_op = "windows"
+
+    op_system = {"Windows": windows_op,
+                 "Linux": "linux-gnu",
+                 "Darwin": "apple-darwin",
+                 "Android": "linux-android",
+                 "Macos": "apple-darwin",
+                 "iOS": "apple-darwin",
+                 "watchOS": "apple-darwin",
+                 "tvOS": "apple-darwin"}.get(os, os.lower())
+
+    if os in ("Linux", "Android"):
+        if "arm" in arch and arch != "armv8":
+            op_system += "eabi"
+
+        if arch == "armv7hf" and os == "Linux":
+            op_system += "hf"
+
+    return "%s-%s" % (machine, op_system)
+
+
 try:
     os_info = OSInfo()
 except Exception as exc:


Test patch:
diff --git a/conans/test/build_helpers/autotools_configure_test.py b/conans/test/build_helpers/autotools_configure_test.py
--- a/conans/test/build_helpers/autotools_configure_test.py
+++ b/conans/test/build_helpers/autotools_configure_test.py
@@ -379,142 +379,14 @@ def modify_values_test(self):
 
     def test_previous_env(self):
         settings = MockSettings({"arch": "x86",
-                                 "os": "Linux"})
+                                 "os": "Linux",
+                                 "compiler": "gcc"})
         conanfile = MockConanfile(settings)
 
         with tools.environment_append({"CPPFLAGS": "MyCppFlag"}):
             be = AutoToolsBuildEnvironment(conanfile)
             self.assertEquals(be.vars["CPPFLAGS"], "MyCppFlag")
 
-    def cross_build_flags_test(self):
-
-        def get_values(this_os, this_arch, setting_os, setting_arch, compiler=None):
-            settings = MockSettings({"arch": setting_arch,
-                                     "os": setting_os,
-                                     "compiler": compiler})
-            conanfile = MockConanfile(settings)
-            conanfile.settings = settings
-            be = AutoToolsBuildEnvironment(conanfile)
-            return be._get_host_build_target_flags(this_arch, this_os)
-
-        build, host, target = get_values("Linux", "x86_64", "Linux", "armv7hf")
-        self.assertEquals(build, "x86_64-linux-gnu")
-        self.assertEquals(host, "arm-linux-gnueabihf")
-
-        build, host, target = get_values("Linux", "x86", "Linux", "armv7hf")
-        self.assertEquals(build, "x86-linux-gnu")
-        self.assertEquals(host, "arm-linux-gnueabihf")
-
-        build, host, target = get_values("Linux", "x86", "Linux", "x86")
-        self.assertFalse(build)
-        self.assertFalse(host)
-        self.assertFalse(target)
-
-        build, host, target = get_values("Linux", "x86_64", "Linux", "x86_64")
-        self.assertFalse(build)
-        self.assertFalse(host)
-        self.assertFalse(target)
-
-        build, host, target = get_values("Linux", "x86_64", "Linux", "x86")
-        self.assertEquals(build, "x86_64-linux-gnu")
-        self.assertEquals(host, "x86-linux-gnu")
-        self.assertFalse(target)
-
-        build, host, target = get_values("Linux", "x86_64", "Windows", "x86", compiler="gcc")
-        self.assertEquals(build, "x86_64-linux-gnu")
-        self.assertEquals(host, "i686-w64-mingw32")
-        self.assertFalse(target)
-
-        build, host, target = get_values("Linux", "x86_64", "Windows", "x86", compiler="Visual Studio")
-        self.assertEquals(build, "x86_64-linux-gnu")
-        self.assertEquals(host, "i686-windows-msvc")  # Not very common but exists sometimes
-        self.assertFalse(target)
-
-        build, host, target = get_values("Linux", "x86_64", "Linux", "armv7hf")
-        self.assertEquals(build, "x86_64-linux-gnu")
-        self.assertEquals(host, "arm-linux-gnueabihf")
-
-        build, host, target = get_values("Linux", "x86_64", "Linux", "armv7")
-        self.assertEquals(build, "x86_64-linux-gnu")
-        self.assertEquals(host, "arm-linux-gnueabi")
-
-        build, host, target = get_values("Linux", "x86_64", "Linux", "armv6")
-        self.assertEquals(build, "x86_64-linux-gnu")
-        self.assertEquals(host, "arm-linux-gnueabi")
-
-        build, host, target = get_values("Linux", "x86_64", "Android", "x86")
-        self.assertEquals(build, "x86_64-linux-gnu")
-        self.assertEquals(host, "i686-linux-android")
-
-        build, host, target = get_values("Linux", "x86_64", "Android", "x86_64")
-        self.assertEquals(build, "x86_64-linux-gnu")
-        self.assertEquals(host, "x86_64-linux-android")
-
-        build, host, target = get_values("Linux", "x86_64", "Android", "armv7")
-        self.assertEquals(build, "x86_64-linux-gnu")
-        self.assertEquals(host, "arm-linux-androideabi")
-
-        build, host, target = get_values("Linux", "x86_64", "Android", "armv7hf")
-        self.assertEquals(build, "x86_64-linux-gnu")
-        self.assertEquals(host, "arm-linux-androideabi")
-
-        build, host, target = get_values("Linux", "x86_64", "Android", "armv8")
-        self.assertEquals(build, "x86_64-linux-gnu")
-        self.assertEquals(host, "aarch64-linux-android")
-
-        build, host, target = get_values("Linux", "x86_64", "Android", "armv6")
-        self.assertEquals(build, "x86_64-linux-gnu")
-        self.assertEquals(host, "arm-linux-androideabi")
-
-        build, host, target = get_values("Linux", "x86_64", "Windows", "x86", compiler="gcc")
-        self.assertEquals(build, "x86_64-linux-gnu")
-        self.assertEquals(host, "i686-w64-mingw32")
-
-        build, host, target = get_values("Linux", "x86_64", "Windows", "x86_64", compiler="gcc")
-        self.assertEquals(build, "x86_64-linux-gnu")
-        self.assertEquals(host, "x86_64-w64-mingw32")
-
-        build, host, target = get_values("Windows", "x86_64", "Windows", "x86_64")
-        self.assertFalse(build)
-        self.assertFalse(host)
-        self.assertFalse(target)
-
-        build, host, target = get_values("Windows", "x86", "Windows", "x86")
-        self.assertFalse(build)
-        self.assertFalse(host)
-        self.assertFalse(target)
-
-        build, host, target = get_values("Windows", "x86_64", "Windows", "x86", compiler="gcc")
-        self.assertEquals(build, "x86_64-w64-mingw32")
-        self.assertEquals(host, "i686-w64-mingw32")
-        self.assertFalse(target)
-
-        build, host, target = get_values("Windows", "x86_64", "Linux", "armv7hf", compiler="gcc")
-        self.assertEquals(build, "x86_64-w64-mingw32")
-        self.assertEquals(host, "arm-linux-gnueabihf")
-        self.assertFalse(target)
-
-        build, host, target = get_values("Darwin", "x86_64", "Android", "armv7hf")
-
-        self.assertEquals(build, "x86_64-apple-darwin")
-        self.assertEquals(host, "arm-linux-androideabi")
-
-        build, host, target = get_values("Darwin", "x86_64", "Macos", "x86")
-        self.assertEquals(build, "x86_64-apple-darwin")
-        self.assertEquals(host, "i686-apple-darwin")
-
-        build, host, target = get_values("Darwin", "x86_64", "iOS", "armv7")
-        self.assertEquals(build, "x86_64-apple-darwin")
-        self.assertEquals(host, "arm-apple-darwin")
-
-        build, host, target = get_values("Darwin", "x86_64", "watchOS", "armv7k")
-        self.assertEquals(build, "x86_64-apple-darwin")
-        self.assertEquals(host, "arm-apple-darwin")
-
-        build, host, target = get_values("Darwin", "x86_64", "tvOS", "armv8")
-        self.assertEquals(build, "x86_64-apple-darwin")
-        self.assertEquals(host, "aarch64-apple-darwin")
-
     def test_pkg_config_paths(self):
         if platform.system() == "Windows":
             return
@@ -552,6 +424,10 @@ def cross_build_command_test(self):
         runner = RunnerMock()
         conanfile = MockConanfile(MockSettings({}), None, runner)
         ab = AutoToolsBuildEnvironment(conanfile)
+        self.assertFalse(ab.build)
+        self.assertFalse(ab.host)
+        self.assertFalse(ab.target)
+
         ab.configure()
         self.assertEquals(runner.command_called, "./configure  ")
 
@@ -564,13 +440,106 @@ def cross_build_command_test(self):
         ab.configure(target="i686-apple-darwin")
         self.assertEquals(runner.command_called, "./configure  --target=i686-apple-darwin")
 
-    def test_make_targets(self):
+        conanfile = MockConanfile(MockSettings({"build_type": "Debug",
+                                                "arch": "x86_64",
+                                                "os": "Windows",
+                                                "compiler": "gcc",
+                                                "compiler.libcxx": "libstdc++"}),
+                                  None, runner)
+        ab = AutoToolsBuildEnvironment(conanfile)
+        ab.configure()
+        if platform.system() == "Windows":
+            # Not crossbuilding
+            self.assertFalse(ab.host)
+            self.assertFalse(ab.build)
+            self.assertIn("./configure", runner.command_called)
+            self.assertNotIn("--build=x86_64-w64-mingw32 --host=x86_64-w64-mingw32",
+                             runner.command_called)
+        elif platform.system() == "Linux":
+            self.assertIn("x86_64-w64-mingw32", ab.host)
+            self.assertIn("x86_64-linux-gnu", ab.build)
+            self.assertIn("./configure  --build=x86_64-linux-gnu --host=x86_64-w64-mingw32",
+                          runner.command_called)
+        else:
+            self.assertIn("x86_64-w64-mingw32", ab.host)
+            self.assertIn("x86_64-apple-darwin", ab.build)
+            self.assertIn("./configure  --build=x86_64-apple-darwin --host=x86_64-w64-mingw32",
+                          runner.command_called)
+
+        ab.configure(build="fake_build_triplet", host="fake_host_triplet")
+        self.assertIn("./configure  --build=fake_build_triplet --host=fake_host_triplet",
+                      runner.command_called)
+
+        ab.build = "superfake_build_triplet"
+        ab.host = "superfake_host_triplet"
+        ab.configure()
+        self.assertIn("./configure  --build=superfake_build_triplet --host=superfake_host_triplet",
+                      runner.command_called)
+
+    def test_make_targets_install(self):
         runner = RunnerMock()
         conanfile = MockConanfile(MockSettings({}), None, runner)
-        
+
         ab = AutoToolsBuildEnvironment(conanfile)
         ab.configure()
-        
+
         ab.make(target="install")
-        self.assertEquals(runner.command_called,"make install -j%s" % cpu_count())        
+        self.assertEquals(runner.command_called, "make install -j%s" % cpu_count())
+        ab.install()
+        self.assertEquals(runner.command_called, "make install -j%s" % cpu_count())
+
+    def autotools_prefix_test(self):
+        runner = RunnerMock()
+        conanfile = MockConanfile(MockSettings({}), None, runner)
+        # Package folder is not defined
+        ab = AutoToolsBuildEnvironment(conanfile)
+        ab.configure()
+        self.assertNotIn("--prefix", runner.command_called)
+        # package folder defined
+        conanfile.package_folder = "/package_folder"
+        ab.configure()
+        if platform.system() == "Windows":
+            self.assertIn("./configure --prefix=/package_folder", runner.command_called)
+        else:
+            self.assertIn("./configure '--prefix=/package_folder'", runner.command_called)
+        # --prefix already used in args
+        ab.configure(args=["--prefix=/my_package_folder"])
+        if platform.system() == "Windows":
+            self.assertIn("./configure --prefix=/my_package_folder", runner.command_called)
+            self.assertNotIn("--prefix=/package_folder", runner.command_called)
+        else:
+            self.assertIn("./configure '--prefix=/my_package_folder'", runner.command_called)
+            self.assertNotIn("'--prefix=/package_folder'", runner.command_called)
+
+    def autotools_configure_vars_test(self):
+        from mock import patch
+
+        runner = RunnerMock()
+        settings = MockSettings({"build_type": "Debug",
+                                 "arch": "x86_64",
+                                 "compiler": "gcc",
+                                 "compiler.libcxx": "libstdc++"})
+        conanfile = MockConanfile(settings, None, runner)
+        conanfile.settings = settings
+        self._set_deps_info(conanfile)
+
+        def custom_configure(obj, configure_dir=None, args=None, build=None, host=None, target=None,
+                             pkg_config_paths=None, vars=None):  # @UnusedVariable
+            self.assertNotEqual(obj.vars, vars)
+            return vars or obj.vars
+
+        with patch.object(AutoToolsBuildEnvironment, 'configure', new=custom_configure):
+            be = AutoToolsBuildEnvironment(conanfile)
+
+            # Get vars and modify them
+            my_vars = be.vars
+            my_vars["fake_var"] = "fake"
+            my_vars["super_fake_var"] = "fakefake"
+
+            # TEST with default vars
+            mocked_result = be.configure()
+            self.assertEqual(mocked_result, be.vars)
 
+            # TEST with custom vars
+            mocked_result = be.configure(vars=my_vars)
+            self.assertEqual(mocked_result, my_vars)
diff --git a/conans/test/util/tools_test.py b/conans/test/util/tools_test.py
--- a/conans/test/util/tools_test.py
+++ b/conans/test/util/tools_test.py
@@ -685,6 +685,126 @@ def download_retries_test(self):
         tools.download("https://httpbin.org/basic-auth/user/passwd", dest,
                        headers={"Authorization": "Basic dXNlcjpwYXNzd2Q="}, overwrite=True)
 
+    def get_gnu_triplet_test(self):
+        def get_values(this_os, this_arch, setting_os, setting_arch, compiler=None):
+            build = tools.get_gnu_triplet(this_os, this_arch, compiler)
+            host = tools.get_gnu_triplet(setting_os, setting_arch, compiler)
+            return build, host
+
+        build, host = get_values("Linux", "x86_64", "Linux", "armv7hf")
+        self.assertEquals(build, "x86_64-linux-gnu")
+        self.assertEquals(host, "arm-linux-gnueabihf")
+
+        build, host = get_values("Linux", "x86", "Linux", "armv7hf")
+        self.assertEquals(build, "x86-linux-gnu")
+        self.assertEquals(host, "arm-linux-gnueabihf")
+
+        build, host = get_values("Linux", "x86_64", "Linux", "x86")
+        self.assertEquals(build, "x86_64-linux-gnu")
+        self.assertEquals(host, "x86-linux-gnu")
+
+        build, host = get_values("Linux", "x86_64", "Windows", "x86", compiler="gcc")
+        self.assertEquals(build, "x86_64-linux-gnu")
+        self.assertEquals(host, "i686-w64-mingw32")
+
+        build, host = get_values("Linux", "x86_64", "Windows", "x86", compiler="Visual Studio")
+        self.assertEquals(build, "x86_64-linux-gnu")
+        self.assertEquals(host, "i686-windows-msvc")  # Not very common but exists sometimes
+
+        build, host = get_values("Linux", "x86_64", "Linux", "armv7hf")
+        self.assertEquals(build, "x86_64-linux-gnu")
+        self.assertEquals(host, "arm-linux-gnueabihf")
+
+        build, host = get_values("Linux", "x86_64", "Linux", "armv7")
+        self.assertEquals(build, "x86_64-linux-gnu")
+        self.assertEquals(host, "arm-linux-gnueabi")
+
+        build, host = get_values("Linux", "x86_64", "Linux", "armv6")
+        self.assertEquals(build, "x86_64-linux-gnu")
+        self.assertEquals(host, "arm-linux-gnueabi")
+
+        build, host = get_values("Linux", "x86_64", "Android", "x86")
+        self.assertEquals(build, "x86_64-linux-gnu")
+        self.assertEquals(host, "i686-linux-android")
+
+        build, host = get_values("Linux", "x86_64", "Android", "x86_64")
+        self.assertEquals(build, "x86_64-linux-gnu")
+        self.assertEquals(host, "x86_64-linux-android")
+
+        build, host = get_values("Linux", "x86_64", "Android", "armv7")
+        self.assertEquals(build, "x86_64-linux-gnu")
+        self.assertEquals(host, "arm-linux-androideabi")
+
+        build, host = get_values("Linux", "x86_64", "Android", "armv7hf")
+        self.assertEquals(build, "x86_64-linux-gnu")
+        self.assertEquals(host, "arm-linux-androideabi")
+
+        build, host = get_values("Linux", "x86_64", "Android", "armv8")
+        self.assertEquals(build, "x86_64-linux-gnu")
+        self.assertEquals(host, "aarch64-linux-android")
+
+        build, host = get_values("Linux", "x86_64", "Android", "armv6")
+        self.assertEquals(build, "x86_64-linux-gnu")
+        self.assertEquals(host, "arm-linux-androideabi")
+
+        build, host = get_values("Linux", "x86_64", "Windows", "x86", compiler="gcc")
+        self.assertEquals(build, "x86_64-linux-gnu")
+        self.assertEquals(host, "i686-w64-mingw32")
+
+        build, host = get_values("Linux", "x86_64", "Windows", "x86_64", compiler="gcc")
+        self.assertEquals(build, "x86_64-linux-gnu")
+        self.assertEquals(host, "x86_64-w64-mingw32")
+
+        build, host = get_values("Windows", "x86_64", "Windows", "x86", compiler="gcc")
+        self.assertEquals(build, "x86_64-w64-mingw32")
+        self.assertEquals(host, "i686-w64-mingw32")
+
+        build, host = get_values("Windows", "x86_64", "Linux", "armv7hf", compiler="gcc")
+        self.assertEquals(build, "x86_64-w64-mingw32")
+        self.assertEquals(host, "arm-linux-gnueabihf")
+
+        build, host = get_values("Darwin", "x86_64", "Android", "armv7hf")
+        self.assertEquals(build, "x86_64-apple-darwin")
+        self.assertEquals(host, "arm-linux-androideabi")
+
+        build, host = get_values("Darwin", "x86_64", "Macos", "x86")
+        self.assertEquals(build, "x86_64-apple-darwin")
+        self.assertEquals(host, "i686-apple-darwin")
+
+        build, host = get_values("Darwin", "x86_64", "iOS", "armv7")
+        self.assertEquals(build, "x86_64-apple-darwin")
+        self.assertEquals(host, "arm-apple-darwin")
+
+        build, host = get_values("Darwin", "x86_64", "watchOS", "armv7k")
+        self.assertEquals(build, "x86_64-apple-darwin")
+        self.assertEquals(host, "arm-apple-darwin")
+
+        build, host = get_values("Darwin", "x86_64", "tvOS", "armv8")
+        self.assertEquals(build, "x86_64-apple-darwin")
+        self.assertEquals(host, "aarch64-apple-darwin")
+
+        for os in ["Windows", "Linux"]:
+            for arch in ["x86_64", "x86"]:
+                triplet = tools.get_gnu_triplet(os, arch, "gcc")
+
+                output = ""
+                if arch == "x86_64":
+                    output += "x86_64"
+                else:
+                    output += "i686" if os != "Linux" else "x86"
+
+                output += "-"
+                if os == "Windows":
+                    output += "w64-mingw32"
+                else:
+                    output += "linux-gnu"
+
+                self.assertIn(output, triplet)
+
+        # Compiler not specified for os="Windows"
+        with self.assertRaises(ConanException):
+            tools.get_gnu_triplet("Windows", "x86")
+
     def detect_windows_subsystem_test(self):
         # Dont raise test
         result = tools.os_info.detect_windows_subsystem()
diff --git a/conans/test/utils/conanfile.py b/conans/test/utils/conanfile.py
--- a/conans/test/utils/conanfile.py
+++ b/conans/test/utils/conanfile.py
@@ -41,6 +41,8 @@ def __init__(self, settings, options=None, runner=None):
         self.should_build = True
         self.should_install = True
 
+        self.package_folder = None
+
     def run(self, *args, **kwargs):
         if self.runner:
             kwargs["output"] = None