Repository: conan-io/conan
Pull request: Add support for extra configure arguments
Changelog: Feature: Add `tools.gnu:configure_args` conf to GnuToolchain and Autotoolchain generator to allow extra arguments to be added to the configure command.

Docs: https://github.com/conan-io/docs/pull/4100

close #18256

- [x] Refer to the issue that supports this Pull Request.
- [x] If the issue has missing info, explain the purpose/use case/pain/need that covers this Pull Request.
- [x] I've read the [Contributing guide](https://github.com/conan-io/conan/blob/develop2/.github/CONTRIBUTING.md).
- [x] I've followed the PEP8 style guides for Python code.
- [x] I've opened another PR in the Conan docs repo to the ``develop`` branch, documenting this one.


Problem statement:
[feature] Extend configure args for AutotoolsToolchain
### What is your suggestion?

Hello! 

The configuration `tools.cmake.cmaketoolchain:extra_variables` allows extending CMakeToolchain definitions without touching the conanfile.py. This option allows users to customize recipes without needing to open a PR and is equally beneficial to maintainers, as the recipe remains as simple as possible.

The same is not possible when the recipe uses `autotools` + `configure` to build a project; there is no mechanism to inject new `configure_args` without changing the Conan recipe.

It would be great if Conan could have a configuration dedicated to AutootoolsToolchain and Gnutoolchain that extends configure arguments as well. For instance:

`tools.gnu:extra_configure_args: List with arguments to be passed to Autotools configure command`

### Have you read the CONTRIBUTING guide?

- [x] I've read the CONTRIBUTING guide

Hints from issue discussion:
Thanks for your suggestion @uilianries 

While CMake is massively popular, and used as de-facto standard for new projects, Autotools is nowadays mostly for legacy gnu-like projects, so the same level of configurability and extensibility is not expected.

What was the use case? What real world scenario did you find? with any particular ConanCenter package?
@memsharded I can list a few PRs from ConanCenterIndex for instance:

- libmount: Add an option for legacy cross-buildin: https://github.com/conan-io/conan-center-index/pull/27161
- openssl: use_validated_fips : https://github.com/conan-io/conan-center-index/pull/27222
- capnproto: add option for lite: https://github.com/conan-io/conan-center-index/pull/26282
- ffmpeg: add option for decoder: https://github.com/conan-io/conan-center-index/pull/26217
- licurl: Add option to build exec: https://github.com/conan-io/conan-center-index/pull/25022

The OpenSSL is a good example, because it has several options already, and still, we have a few PRs asking for new options, making it impossible to combine and validate the build graph.

The same can be applied to FFmpeg and libcurl. 

I am not sure if I get it. Those really look like options, they change the resulting binary, in a pretty incompatible way.

> The OpenSSL is a good example, because it has several options already, and still, we have a few PRs asking for new options, making it impossible to combine and validate the build graph.

I see, I think it is worth discussing with the team, thanks!

Commit message: Replace extra_configure_args by configure_args

Signed-off-by: Uilian Ries <uilianries@gmail.com>

Patch to review:
diff --git a/conan/internal/model/conf.py b/conan/internal/model/conf.py
index 0b4125bd108..979884de676 100644
--- a/conan/internal/model/conf.py
+++ b/conan/internal/model/conf.py
@@ -101,6 +101,7 @@
     "tools.gnu:pkg_config": "Path to pkg-config executable used by PkgConfig build helper",
     "tools.gnu:build_triplet": "Custom build triplet to pass to Autotools scripts",
     "tools.gnu:host_triplet": "Custom host triplet to pass to Autotools scripts",
+    "tools.gnu:configure_args": "List of extra arguments to pass to configure when using AutotoolsToolchain and GnuToolchain",
     "tools.google.bazel:configs": "List of Bazel configurations to be used as 'bazel build --config=config1 ...'",
     "tools.google.bazel:bazelrc_path": "List of paths to bazelrc files to be used as 'bazel --bazelrc=rcpath1 ... build'",
     "tools.meson.mesontoolchain:backend": "Any Meson backend: ninja, vs, vs2010, vs2012, vs2013, vs2015, vs2017, vs2019, xcode",
diff --git a/conan/tools/gnu/autotoolstoolchain.py b/conan/tools/gnu/autotoolstoolchain.py
index cced955ccec..26aa9984c29 100644
--- a/conan/tools/gnu/autotoolstoolchain.py
+++ b/conan/tools/gnu/autotoolstoolchain.py
@@ -83,10 +83,14 @@ def __init__(self, conanfile, namespace=None, prefix="/"):
         sysroot = self._conanfile.conf.get("tools.build:sysroot")
         sysroot = sysroot.replace("\\", "/") if sysroot is not None else None
         self.sysroot_flag = "--sysroot {}".format(sysroot) if sysroot else None
+        extra_configure_args = self._conanfile.conf.get("tools.gnu:configure_args",
+                                                        check_type=list,
+                                                        default=[])
 
         self.configure_args = (self._default_configure_shared_flags() +
                                self._default_configure_install_flags() +
-                               self._get_triplets())
+                               self._get_triplets() +
+                               extra_configure_args)
         self.autoreconf_args = self._default_autoreconf_flags()
         self.make_args = []
         # Apple stuff
diff --git a/conan/tools/gnu/gnutoolchain.py b/conan/tools/gnu/gnutoolchain.py
index aa2897005e3..4c660aea647 100644
--- a/conan/tools/gnu/gnutoolchain.py
+++ b/conan/tools/gnu/gnutoolchain.py
@@ -62,6 +62,10 @@ def __init__(self, conanfile, namespace=None, prefix="/"):
         self.fpic = self._conanfile.options.get_safe("fPIC")
         self.msvc_runtime_flag = self._get_msvc_runtime_flag()
         self.msvc_extra_flags = self._msvc_extra_flags()
+        extra_configure_args = self._conanfile.conf.get("tools.gnu:configure_args",
+                                                        check_type=list,
+                                                        default=[])
+        extra_configure_args = {it: None for it in extra_configure_args}
 
         # Host/Build triplets
         self.triplets_info = {
@@ -92,6 +96,7 @@ def __init__(self, conanfile, namespace=None, prefix="/"):
         self.configure_args.update(self._get_default_configure_shared_flags())
         self.configure_args.update(self._get_default_configure_install_flags())
         self.configure_args.update(self._get_default_triplets())
+        self.configure_args.update(extra_configure_args)
         # Apple stuff
         is_cross_building_osx = (self._is_cross_building
                                  and conanfile.settings_build.get_safe('os') == "Macos"
diff --git a/test/unittests/client/toolchain/autotools/autotools_toolchain_test.py b/test/unittests/client/toolchain/autotools/autotools_toolchain_test.py
index 0879043d03c..683cb4482b6 100644
--- a/test/unittests/client/toolchain/autotools/autotools_toolchain_test.py
+++ b/test/unittests/client/toolchain/autotools/autotools_toolchain_test.py
@@ -605,3 +605,29 @@ def test_conf_compiler_executable():
     be = AutotoolsToolchain(conanfile)
     env = be.vars()
     assert "/c/my/path/myg++" == env["CXX"]
+
+
+def test_autotools_toolchain_conf_configure_args():
+    """Validate that tools.gnu:configure_args are passed to configure command only.
+
+       The configure args should be passed as list only.
+    """
+    f = temp_folder()
+    os.chdir(f)
+    conanfile = ConanFileMock()
+    conanfile.settings = MockSettings({"os": "Linux", "arch": "x86_64"})
+    conanfile.conf = Conf()
+    conanfile.conf.define("tools.gnu:configure_args", ["--foo", "--bar"])
+
+    be = AutotoolsToolchain(conanfile)
+    be.generate_args()
+    obj = load_toolchain_args()
+    assert "--foo --bar" in obj["configure_args"]
+    # make sure it does not forward to make
+    assert "--foo" not in obj["make_args"]
+
+    conanfile.conf.define("tools.gnu:configure_args", "--foo --bar")
+    with pytest.raises(ConanException) as expected:
+        AutotoolsToolchain(conanfile)
+    assert "[conf] tools.gnu:configure_args must be a list-like object. "\
+           "The value '--foo --bar' introduced is a str object" in str(expected)
diff --git a/test/unittests/tools/gnu/test_gnutoolchain.py b/test/unittests/tools/gnu/test_gnutoolchain.py
index a0e545d8b3d..e7395ac7bc5 100644
--- a/test/unittests/tools/gnu/test_gnutoolchain.py
+++ b/test/unittests/tools/gnu/test_gnutoolchain.py
@@ -235,3 +235,24 @@ def test_crossbuild_to_android(build_env_mock):
     assert gnutc.triplets_info["host"]["triplet"] == "aarch64-linux-android"
     assert env_vars["LD"] == os.path.join(ndk_bin, "ld")  # exists
     assert env_vars["STRIP"] == os.path.join(ndk_bin, "llvm-strip")  # does not exist but appears
+
+
+def test_gnu_toolchain_conf_configure_args():
+    """ Validate that tools.gnu:configure_args are passed to the configure_args when
+        building with GnuToolchain.
+        The configure args should be passed as a list-like object.
+    """
+    conanfile = ConanFileMock()
+    conanfile.settings = MockSettings({"os": "Linux", "arch": "x86_64"})
+    conanfile.conf = Conf()
+    conanfile.conf.define("tools.gnu:configure_args", ["--foo", "--bar"])
+
+    tc = GnuToolchain(conanfile)
+    assert tc.configure_args["--foo"] is None
+    assert tc.configure_args["--bar"] is None
+
+    conanfile.conf.define("tools.gnu:configure_args", "--foo --bar")
+    with pytest.raises(ConanException) as expected:
+        GnuToolchain(conanfile)
+    assert "[conf] tools.gnu:configure_args must be a list-like object. " \
+           "The value '--foo --bar' introduced is a str object" in str(expected)


Code review comments:
diff_hunk: @@ -235,3 +235,24 @@ def test_crossbuild_to_android(build_env_mock):
     assert gnutc.triplets_info["host"]["triplet"] == "aarch64-linux-android"
     assert env_vars["LD"] == os.path.join(ndk_bin, "ld")  # exists
     assert env_vars["STRIP"] == os.path.join(ndk_bin, "llvm-strip")  # does not exist but appears
+
+
+def test_gnu_toolchain_conf_configure_args():
+    """ Validate that tools.gnu:configure_args are passed to the configure_args when
+        building with GnuToolchain.
+        The configure args should be passed as a list-like object.
+    """
+    conanfile = ConanFileMock()
+    conanfile.settings = MockSettings({"os": "Linux", "arch": "x86_64"})
+    conanfile.conf = Conf()
+    conanfile.conf.define("tools.gnu:configure_args", ["--foo", "--bar"])
+
+    tc = GnuToolchain(conanfile)
+    assert tc.configure_args["--foo"] is None
+    assert tc.configure_args["--bar"] is None
+
+    conanfile.conf.define("tools.gnu:configure_args", "--foo --bar")
+    with pytest.raises(ConanException) as expected:
+        GnuToolchain(conanfile)
+    assert "[conf] tools.gnu:configure_args must be a list-like object. " \
+           "The value '--foo --bar' introduced is a str object" in str(expected)
path: test/unittests/tools/gnu/test_gnutoolchain.py
text: @user1:
I think that we can remove this check as it's part of the `conf.get(..., check_type="")` behavior:

```suggestion
```

diff_hunk: @@ -605,3 +605,29 @@ def test_conf_compiler_executable():
     be = AutotoolsToolchain(conanfile)
     env = be.vars()
     assert "/c/my/path/myg++" == env["CXX"]
+
+
+def test_autotools_toolchain_conf_configure_args():
+    """Validate that tools.gnu:configure_args are passed to configure command only.
+
+       The configure args should be passed as list only.
+    """
+    f = temp_folder()
+    os.chdir(f)
+    conanfile = ConanFileMock()
+    conanfile.settings = MockSettings({"os": "Linux", "arch": "x86_64"})
+    conanfile.conf = Conf()
+    conanfile.conf.define("tools.gnu:configure_args", ["--foo", "--bar"])
+
+    be = AutotoolsToolchain(conanfile)
+    be.generate_args()
+    obj = load_toolchain_args()
+    assert "--foo --bar" in obj["configure_args"]
+    # make sure it does not forward to make
+    assert "--foo" not in obj["make_args"]
+
+    conanfile.conf.define("tools.gnu:configure_args", "--foo --bar")
+    with pytest.raises(ConanException) as expected:
+        AutotoolsToolchain(conanfile)
+    assert "[conf] tools.gnu:configure_args must be a list-like object. "\
+           "The value '--foo --bar' introduced is a str object" in str(expected)
path: test/unittests/client/toolchain/autotools/autotools_toolchain_test.py
text: @user1:
I think that we can remove this check as it's part of the `conf.get(..., check_type="")` behavior:

```suggestion
```