diff options
author | Carlos Zoido <mrgalleta@gmail.com> | 2021-09-29 12:11:13 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-29 12:11:13 +0200 |
commit | 7532ccc7bb2968b47310fc7a183d809cc70f7f07 (patch) | |
tree | 99098fa7f9eb152b706dfe9f9b0026bf060f7028 | |
parent | 5b283dcbfefddd4041acbbe949ee412f5d14c2f4 (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.py | 5 | ||||
-rw-r--r-- | conan/tools/cmake/toolchain.py | 5 | ||||
-rw-r--r-- | conan/tools/files/files.py | 14 | ||||
-rw-r--r-- | conan/tools/gnu/autotools.py | 5 | ||||
-rw-r--r-- | conan/tools/gnu/autotoolstoolchain.py | 5 | ||||
-rw-r--r-- | conan/tools/google/bazel.py | 6 | ||||
-rw-r--r-- | conan/tools/google/toolchain.py | 5 | ||||
-rw-r--r-- | conans/test/integration/toolchains/test_toolchain_namespaces.py | 170 |
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 |