summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCarlos Zoido <mrgalleta@gmail.com>2021-09-29 12:11:13 +0200
committerGitHub <noreply@github.com>2021-09-29 12:11:13 +0200
commit7532ccc7bb2968b47310fc7a183d809cc70f7f07 (patch)
tree99098fa7f9eb152b706dfe9f9b0026bf060f7028
parent5b283dcbfefddd4041acbbe949ee412f5d14c2f4 (diff)
Support multiple toolchains in one recipe (#9688)
* add namespace to file tools * add to cmake * add autotools and bazel * move tests * remove test * remove imports * minor changes
-rw-r--r--conan/tools/cmake/cmake.py5
-rw-r--r--conan/tools/cmake/toolchain.py5
-rw-r--r--conan/tools/files/files.py14
-rw-r--r--conan/tools/gnu/autotools.py5
-rw-r--r--conan/tools/gnu/autotoolstoolchain.py5
-rw-r--r--conan/tools/google/bazel.py6
-rw-r--r--conan/tools/google/toolchain.py5
-rw-r--r--conans/test/integration/toolchains/test_toolchain_namespaces.py170
8 files changed, 199 insertions, 16 deletions
diff --git a/conan/tools/cmake/cmake.py b/conan/tools/cmake/cmake.py
index 40ceba2e..6afe2af6 100644
--- a/conan/tools/cmake/cmake.py
+++ b/conan/tools/cmake/cmake.py
@@ -57,14 +57,15 @@ class CMake(object):
are passed to the command line, plus the ``--config Release`` for builds in multi-config
"""
- def __init__(self, conanfile, parallel=True):
+ def __init__(self, conanfile, parallel=True, namespace=None):
_validate_recipe(conanfile)
# Store a reference to useful data
self._conanfile = conanfile
self._parallel = parallel
+ self._namespace = namespace
- toolchain_file_content = load_toolchain_args(self._conanfile.generators_folder)
+ toolchain_file_content = load_toolchain_args(self._conanfile.generators_folder, namespace=self._namespace)
self._generator = toolchain_file_content.get("cmake_generator")
self._toolchain_file = toolchain_file_content.get("cmake_toolchain_file")
diff --git a/conan/tools/cmake/toolchain.py b/conan/tools/cmake/toolchain.py
index 3c3c0f75..90a00c59 100644
--- a/conan/tools/cmake/toolchain.py
+++ b/conan/tools/cmake/toolchain.py
@@ -712,9 +712,10 @@ class CMakeToolchain(object):
{{ iterate_configs(preprocessor_definitions_config, action='add_definitions') }}
""")
- def __init__(self, conanfile, generator=None):
+ def __init__(self, conanfile, generator=None, namespace=None):
self._conanfile = conanfile
self.generator = self._get_generator(generator)
+ self._namespace = namespace
self.variables = Variables()
self.preprocessor_definitions = Variables()
@@ -779,7 +780,7 @@ class CMakeToolchain(object):
result["cmake_toolchain_file"] = toolchain_file or self.filename
if result:
- save_toolchain_args(result)
+ save_toolchain_args(result, namespace=self._namespace)
def _get_generator(self, recipe_generator):
# Returns the name of the generator to be used by CMake
diff --git a/conan/tools/files/files.py b/conan/tools/files/files.py
index 3435a414..0823d043 100644
--- a/conan/tools/files/files.py
+++ b/conan/tools/files/files.py
@@ -186,15 +186,18 @@ def rename(conanfile, src, dst):
raise ConanException("rename {} to {} failed: {}".format(src, dst, err))
-def load_toolchain_args(generators_folder=None):
+def load_toolchain_args(generators_folder=None, namespace=None):
"""
Helper function to load the content of any CONAN_TOOLCHAIN_ARGS_FILE
:param generators_folder: `str` folder where is located the CONAN_TOOLCHAIN_ARGS_FILE.
+ :param namespace: `str` namespace to be prepended to the filename.
:return: <class 'configparser.SectionProxy'>
"""
- args_file = os.path.join(generators_folder, CONAN_TOOLCHAIN_ARGS_FILE) if generators_folder \
+ namespace_name = "{}_{}".format(namespace, CONAN_TOOLCHAIN_ARGS_FILE) if namespace \
else CONAN_TOOLCHAIN_ARGS_FILE
+ args_file = os.path.join(generators_folder, namespace_name) if generators_folder \
+ else namespace_name
toolchain_config = configparser.ConfigParser()
toolchain_file = toolchain_config.read(args_file)
if not toolchain_file:
@@ -208,17 +211,20 @@ def load_toolchain_args(generators_folder=None):
(CONAN_TOOLCHAIN_ARGS_SECTION, args_file))
-def save_toolchain_args(content, generators_folder=None):
+def save_toolchain_args(content, generators_folder=None, namespace=None):
"""
Helper function to save the content into the CONAN_TOOLCHAIN_ARGS_FILE
:param content: `dict` all the information to be saved into the toolchain file.
+ :param namespace: `str` namespace to be prepended to the filename.
:param generators_folder: `str` folder where is located the CONAN_TOOLCHAIN_ARGS_FILE
"""
# Let's prune None values
content_ = {k: v for k, v in content.items() if v is not None}
- args_file = os.path.join(generators_folder, CONAN_TOOLCHAIN_ARGS_FILE) if generators_folder \
+ namespace_name = "{}_{}".format(namespace, CONAN_TOOLCHAIN_ARGS_FILE) if namespace \
else CONAN_TOOLCHAIN_ARGS_FILE
+ args_file = os.path.join(generators_folder, namespace_name) if generators_folder \
+ else namespace_name
toolchain_config = configparser.ConfigParser()
toolchain_config[CONAN_TOOLCHAIN_ARGS_SECTION] = content_
with open(args_file, "w") as f:
diff --git a/conan/tools/gnu/autotools.py b/conan/tools/gnu/autotools.py
index d28a9dbd..1135b544 100644
--- a/conan/tools/gnu/autotools.py
+++ b/conan/tools/gnu/autotools.py
@@ -7,10 +7,11 @@ from conans.client.build import join_arguments
class Autotools(object):
- def __init__(self, conanfile):
+ def __init__(self, conanfile, namespace=None):
self._conanfile = conanfile
- toolchain_file_content = load_toolchain_args(self._conanfile.generators_folder)
+ toolchain_file_content = load_toolchain_args(self._conanfile.generators_folder,
+ namespace=namespace)
self._configure_args = toolchain_file_content.get("configure_args")
self._make_args = toolchain_file_content.get("make_args")
diff --git a/conan/tools/gnu/autotoolstoolchain.py b/conan/tools/gnu/autotoolstoolchain.py
index 11fb747b..2c667adc 100644
--- a/conan/tools/gnu/autotoolstoolchain.py
+++ b/conan/tools/gnu/autotoolstoolchain.py
@@ -10,8 +10,9 @@ from conans.tools import args_to_string
class AutotoolsToolchain:
- def __init__(self, conanfile):
+ def __init__(self, conanfile, namespace=None):
self._conanfile = conanfile
+ self._namespace = namespace
build_type = self._conanfile.settings.get_safe("build_type")
self.configure_args = []
@@ -165,4 +166,4 @@ class AutotoolsToolchain:
args = {"configure_args": args_to_string(configure_args),
"make_args": args_to_string(self.make_args)}
- save_toolchain_args(args)
+ save_toolchain_args(args, namespace=self._namespace)
diff --git a/conan/tools/google/bazel.py b/conan/tools/google/bazel.py
index 804eec1b..574ded1b 100644
--- a/conan/tools/google/bazel.py
+++ b/conan/tools/google/bazel.py
@@ -2,8 +2,9 @@ from conan.tools.files import load_toolchain_args
class Bazel(object):
- def __init__(self, conanfile):
+ def __init__(self, conanfile, namespace=None):
self._conanfile = conanfile
+ self._namespace = namespace
self._get_bazel_project_configuration()
def configure(self, args=None):
@@ -37,6 +38,7 @@ class Bazel(object):
self._conanfile.run(command)
def _get_bazel_project_configuration(self):
- toolchain_file_content = load_toolchain_args(self._conanfile.generators_folder)
+ toolchain_file_content = load_toolchain_args(self._conanfile.generators_folder,
+ namespace=self._namespace)
self._bazel_config = toolchain_file_content.get("bazel_config")
self._bazelrc_path = toolchain_file_content.get("bazelrc_path")
diff --git a/conan/tools/google/toolchain.py b/conan/tools/google/toolchain.py
index 2adc9918..4ef15ab7 100644
--- a/conan/tools/google/toolchain.py
+++ b/conan/tools/google/toolchain.py
@@ -4,12 +4,13 @@ from conan.tools.files import save_toolchain_args
class BazelToolchain(object):
- def __init__(self, conanfile):
+ def __init__(self, conanfile, namespace=None):
self._conanfile = conanfile
+ self._namespace = namespace
check_using_build_profile(self._conanfile)
def generate(self):
save_toolchain_args({
"bazel_config": self._conanfile.conf["tools.google.bazel:config"],
"bazelrc_path": self._conanfile.conf["tools.google.bazel:bazelrc_path"]
- })
+ }, namespace=self._namespace)
diff --git a/conans/test/integration/toolchains/test_toolchain_namespaces.py b/conans/test/integration/toolchains/test_toolchain_namespaces.py
new file mode 100644
index 00000000..178915aa
--- /dev/null
+++ b/conans/test/integration/toolchains/test_toolchain_namespaces.py
@@ -0,0 +1,170 @@
+import os
+import textwrap
+
+from conan.tools import CONAN_TOOLCHAIN_ARGS_FILE
+from conan.tools.files import load_toolchain_args
+from conans.test.utils.tools import TestClient
+
+
+def test_cmake_namespace():
+ client = TestClient()
+ namespace = "somename"
+ conanfile = textwrap.dedent("""
+ from conans import ConanFile
+ from conan.tools.cmake import CMakeToolchain, CMake
+
+ class Conan(ConanFile):
+ settings = "os", "arch", "compiler", "build_type"
+ def generate(self):
+ cmake = CMakeToolchain(self, namespace='{0}')
+ cmake.generate()
+ def build(self):
+ cmake = CMake(self, namespace='{0}')
+ self.output.info(cmake._generator)
+ self.output.info(cmake._toolchain_file)
+ """.format(namespace))
+
+ client.save({"conanfile.py": conanfile})
+ client.run("install . ")
+ assert os.path.isfile(os.path.join(client.current_folder,
+ "{}_{}".format(namespace, CONAN_TOOLCHAIN_ARGS_FILE)))
+ content = load_toolchain_args(generators_folder=client.current_folder, namespace=namespace)
+ generator = content.get("cmake_generator")
+ toolchain_file = content.get("cmake_toolchain_file")
+ client.run("build . ")
+ assert generator in client.out
+ assert toolchain_file in client.out
+
+
+def test_bazel_namespace():
+ client = TestClient()
+ namespace = "somename"
+ conanfile = textwrap.dedent("""
+ from conans import ConanFile
+ from conan.tools.google import BazelToolchain, Bazel
+
+ class Conan(ConanFile):
+ settings = "os", "arch", "compiler", "build_type"
+ def generate(self):
+ bazel = BazelToolchain(self, namespace='{0}')
+ bazel.generate()
+ def build(self):
+ bazel = Bazel(self, namespace='{0}')
+ self.output.info(bazel._bazel_config)
+ self.output.info(bazel._bazelrc_path)
+ """.format(namespace))
+
+ profile = textwrap.dedent("""
+ include(default)
+ [conf]
+ tools.google.bazel:config=test_config
+ tools.google.bazel:bazelrc_path=/path/to/bazelrc
+ """)
+
+ client.save({"test_profile": profile})
+
+ client.save({"conanfile.py": conanfile})
+ client.run("install . -pr test_profile")
+ assert os.path.isfile(os.path.join(client.current_folder,
+ "{}_{}".format(namespace, CONAN_TOOLCHAIN_ARGS_FILE)))
+ content = load_toolchain_args(generators_folder=client.current_folder, namespace=namespace)
+ bazel_config = content.get("bazel_config")
+ bazelrc_path = content.get("bazelrc_path")
+ client.run("build .")
+ assert bazel_config in client.out
+ assert bazelrc_path in client.out
+
+
+def test_autotools_namespace():
+ client = TestClient()
+ namespace = "somename"
+ conanfile = textwrap.dedent("""
+ from conans import ConanFile
+ from conan.tools.gnu import AutotoolsToolchain, Autotools
+
+ class Conan(ConanFile):
+ settings = "os", "arch", "compiler", "build_type"
+ def generate(self):
+ autotools = AutotoolsToolchain(self, namespace='{0}')
+ autotools.configure_args = ['a', 'b']
+ autotools.make_args = ['c', 'd']
+ autotools.generate()
+ def build(self):
+ autotools = Autotools(self, namespace='{0}')
+ self.output.info(autotools._configure_args)
+ self.output.info(autotools._make_args)
+ """.format(namespace))
+
+ client.save({"conanfile.py": conanfile})
+ client.run("install .")
+ assert os.path.isfile(os.path.join(client.current_folder,
+ "{}_{}".format(namespace, CONAN_TOOLCHAIN_ARGS_FILE)))
+ content = load_toolchain_args(generators_folder=client.current_folder, namespace=namespace)
+ at_configure_args = content.get("configure_args")
+ at_make_args = content.get("make_args")
+ client.run("build .")
+ assert at_configure_args in client.out
+ assert at_make_args in client.out
+
+
+def test_multiple_toolchains_one_recipe():
+ # https://github.com/conan-io/conan/issues/9376
+ client = TestClient()
+ namespaces = ["autotools", "bazel", "cmake"]
+ conanfile = textwrap.dedent("""
+ from conans import ConanFile
+ from conan.tools.gnu import AutotoolsToolchain, Autotools
+ from conan.tools.google import BazelToolchain, Bazel
+ from conan.tools.cmake import CMakeToolchain, CMake
+
+ class Conan(ConanFile):
+ settings = "os", "arch", "compiler", "build_type"
+ def generate(self):
+ autotools = AutotoolsToolchain(self, namespace='{0}')
+ autotools.configure_args = ['a', 'b']
+ autotools.make_args = ['c', 'd']
+ autotools.generate()
+ bazel = BazelToolchain(self, namespace='{1}')
+ bazel.generate()
+ cmake = CMakeToolchain(self, namespace='{2}')
+ cmake.generate()
+
+ def build(self):
+ autotools = Autotools(self, namespace='{0}')
+ self.output.info(autotools._configure_args)
+ self.output.info(autotools._make_args)
+ bazel = Bazel(self, namespace='{1}')
+ self.output.info(bazel._bazel_config)
+ self.output.info(bazel._bazelrc_path)
+ cmake = CMake(self, namespace='{2}')
+ self.output.info(cmake._generator)
+ self.output.info(cmake._toolchain_file)
+ """.format(*namespaces))
+
+ client.save({"conanfile.py": conanfile})
+
+ profile = textwrap.dedent("""
+ include(default)
+ [conf]
+ tools.google.bazel:config=test_config
+ tools.google.bazel:bazelrc_path=/path/to/bazelrc
+ """)
+
+ client.save({"test_profile": profile})
+
+ client.run("install . -pr test_profile")
+ check_args = {
+ "autotools": ["configure_args", "make_args"],
+ "bazel": ["bazel_config", "bazelrc_path"],
+ "cmake": ["cmake_generator", "cmake_toolchain_file"]
+ }
+ checks = []
+ for namespace in namespaces:
+ assert os.path.isfile(os.path.join(client.current_folder,
+ "{}_{}".format(namespace, CONAN_TOOLCHAIN_ARGS_FILE)))
+ content = load_toolchain_args(generators_folder=client.current_folder, namespace=namespace)
+ for arg in check_args.get(namespace):
+ checks.append(content.get(arg))
+ client.run("build .")
+ for check in checks:
+ assert check in client.out