From e15749233ddee886bc7668b1038ceea51d79b1c6 Mon Sep 17 00:00:00 2001 From: "Javier G. Sogo" Date: Wed, 5 Aug 2020 16:30:24 +0200 Subject: [PATCH] [fix] Propagate 'user_info_build' using package development commands (#7488) * investigate #7247 * add 'user_info_build' to TXT generator * handle line length in file * add more data to the idempotent test * add tests in files * remove unneeded file * iterate dict in order * add user_info_build to tests --- conans/client/generators/text.py | 75 ++++++++++++------- conans/client/graph/graph_manager.py | 5 +- conans/model/conan_generator.py | 1 + .../cross_building/user_info_test.py | 49 +++++++----- .../environment/apply_environment_test.py | 2 +- conans/test/functional/graph/diamond_test.py | 2 +- .../client/generators/txt/test_content.py | 23 ++++++ .../client/generators/txt/test_dump_load.py | 18 ++++- .../client/generators/txt/test_load.py | 4 +- .../test/unittests/client/tools/net_test.py | 1 - .../test/unittests/model/build_info_test.py | 12 +-- 11 files changed, 128 insertions(+), 64 deletions(-) diff --git a/conans/client/generators/text.py b/conans/client/generators/text.py index d256b1ce4a6..9b217ebc7c9 100644 --- a/conans/client/generators/text.py +++ b/conans/client/generators/text.py @@ -47,6 +47,8 @@ def __init__(self, cpp_info): class TXTGenerator(Generator): name = "txt" + _USER_INFO_HOST_PREFIX = "USER" + _USER_INFO_BUILD_PREFIX = "USERBUILD" @property def filename(self): @@ -54,42 +56,50 @@ def filename(self): @staticmethod def loads(text, filter_empty=False): - user_defines_index = text.find("[USER_") - env_defines_index = text.find("[ENV_") - if user_defines_index != -1: - deps_cpp_info_txt = text[:user_defines_index] - if env_defines_index != -1: - user_info_txt = text[user_defines_index:env_defines_index] - deps_env_info_txt = text[env_defines_index:] - else: - user_info_txt = text[user_defines_index:] - deps_env_info_txt = "" - else: - if env_defines_index != -1: - deps_cpp_info_txt = text[:env_defines_index] - deps_env_info_txt = text[env_defines_index:] - else: - deps_cpp_info_txt = text - deps_env_info_txt = "" + user_info_host_idx = text.find("[{}_".format(TXTGenerator._USER_INFO_HOST_PREFIX)) + deps_env_info_idx = text.find("[ENV_") + user_info_build_idx = text.find("[{}_".format(TXTGenerator._USER_INFO_BUILD_PREFIX)) + + user_info_host_txt = deps_env_info_txt = "" + + # Get chunk with deps_cpp_info: from the beginning to the first one of the others + last_idx = next((x for x in [user_info_host_idx, deps_env_info_idx, user_info_build_idx] + if x != -1), None) + deps_cpp_info_txt = text[:last_idx] - user_info_txt = "" + if user_info_host_idx != -1: + last_idx = next((x for x in [deps_env_info_idx, user_info_build_idx] if x != -1), None) + user_info_host_txt = text[user_info_host_idx:last_idx] + + if deps_env_info_idx != -1: + last_idx = next((x for x in [user_info_build_idx] if x != -1), None) + deps_env_info_txt = text[deps_env_info_idx:last_idx] + + user_info_build = None + if user_info_build_idx != -1: + user_info_build_txt = text[user_info_build_idx:] + user_info_build = TXTGenerator._loads_user_info(user_info_build_txt, + TXTGenerator._USER_INFO_BUILD_PREFIX) deps_cpp_info = TXTGenerator._loads_cpp_info(deps_cpp_info_txt, filter_empty=filter_empty) - deps_user_info = TXTGenerator._loads_deps_user_info(user_info_txt) + deps_user_info = TXTGenerator._loads_user_info(user_info_host_txt, + TXTGenerator._USER_INFO_HOST_PREFIX) deps_env_info = DepsEnvInfo.loads(deps_env_info_txt) - return deps_cpp_info, deps_user_info, deps_env_info + return deps_cpp_info, deps_user_info, deps_env_info, user_info_build @staticmethod - def _loads_deps_user_info(text): + def _loads_user_info(text, user_info_prefix): + _prefix_for_user_info_host = "[{}_".format(user_info_prefix) + _prefix_for_user_info_host_length = len(_prefix_for_user_info_host) ret = DepsUserInfo() lib_name = None for line in text.splitlines(): if not line: continue - if not lib_name and not line.startswith("[USER_"): + if not lib_name and not line.startswith(_prefix_for_user_info_host): raise ConanException("Error, invalid file format reading user info variables") - elif line.startswith("[USER_"): - lib_name = line[6:-1] + elif line.startswith(_prefix_for_user_info_host): + lib_name = line[_prefix_for_user_info_host_length:-1] else: var_name, value = line.split("=", 1) setattr(ret[lib_name], var_name, value) @@ -213,13 +223,20 @@ def content(self): all_flags = template.format(dep=dep, deps=deps, config=":" + config) sections.append(all_flags) - # Generate the user info variables as [USER_{DEP_NAME}] and then the values with key=value - for dep, the_vars in sorted(self._deps_user_info.items()): - sections.append("[USER_%s]" % dep) - for name, value in sorted(the_vars.vars.items()): - sections.append("%s=%s" % (name, value)) + def append_user_info(prefix, user_info_data): + for dep, the_vars in sorted(user_info_data.items()): + sections.append("[{prefix}_{dep_name}]".format(prefix=prefix, dep_name=dep)) + for name, value in sorted(the_vars.vars.items()): + sections.append("%s=%s" % (name, value)) + + # Generate the user_info variables for HOST as [USER_{DEP_NAME}] and values with key=value + append_user_info(self._USER_INFO_HOST_PREFIX, self._deps_user_info) # Generate the env info variables as [ENV_{DEP_NAME}] and then the values with key=value sections.append(self._deps_env_info.dumps()) + # Generate the user_info variables for BUILD as [USERBUILD_{DEP_NAME}] + if self._user_info_build: + append_user_info(self._USER_INFO_BUILD_PREFIX, self._user_info_build) + return "\n".join(sections) diff --git a/conans/client/graph/graph_manager.py b/conans/client/graph/graph_manager.py index ee46764358a..0431f5b0851 100644 --- a/conans/client/graph/graph_manager.py +++ b/conans/client/graph/graph_manager.py @@ -403,10 +403,13 @@ def __getitem__(self, item): return info_file_path = os.path.join(current_path, BUILD_INFO) try: - deps_cpp_info, deps_user_info, deps_env_info = TXTGenerator.loads(load(info_file_path)) + deps_cpp_info, deps_user_info, deps_env_info, user_info_build = \ + TXTGenerator.loads(load(info_file_path)) conanfile.deps_cpp_info = deps_cpp_info conanfile.deps_user_info = deps_user_info conanfile.deps_env_info = deps_env_info + if user_info_build: + conanfile.user_info_build = user_info_build except IOError: if required: raise ConanException("%s file not found in %s\nIt is required for this command\n" diff --git a/conans/model/conan_generator.py b/conans/model/conan_generator.py index fc205ad014b..0a153a58d49 100644 --- a/conans/model/conan_generator.py +++ b/conans/model/conan_generator.py @@ -13,6 +13,7 @@ def __init__(self, conanfile): self._deps_env_info = conanfile.deps_env_info self._env_info = conanfile.env_info self._deps_user_info = conanfile.deps_user_info + self._user_info_build = getattr(conanfile, 'user_info_build', None) @property def deps_build_info(self): diff --git a/conans/test/functional/cross_building/user_info_test.py b/conans/test/functional/cross_building/user_info_test.py index 23007ad7e00..6c85106a439 100644 --- a/conans/test/functional/cross_building/user_info_test.py +++ b/conans/test/functional/cross_building/user_info_test.py @@ -59,25 +59,36 @@ def build(self): _info("[build] br_build.DATA={}".format(self.user_info_build["br_build"].DATA)) """) - def test_user_info_from_requirements(self): - t = TestClient() - t.save({'library.py': self.library, - 'build_requires.py': self.br, - 'app.py': self.app, - 'host': '[settings]\nos=Windows', - 'build': '[settings]\nos=Linux', }) - t.run("create library.py library/1.0@ --profile=host") - t.run("create library.py library/1.0@ --profile=build") - t.run("create build_requires.py br_host/1.0@ --profile=host") - t.run("create build_requires.py br_build/1.0@ --profile=build") - t.run("create app.py app/1.0@ --profile:host=host --profile:build=build") - + @classmethod + def setUpClass(cls): + super(UserInfoTestCase, cls).setUpClass() + cls.t = TestClient() + cls.t.save({'library.py': cls.library, + 'build_requires.py': cls.br, + 'app.py': cls.app, + 'host': '[settings]\nos=Windows', + 'build': '[settings]\nos=Linux', }) + cls.t.run("create library.py library/1.0@ --profile=host") + cls.t.run("create library.py library/1.0@ --profile=build") + cls.t.run("create build_requires.py br_host/1.0@ --profile=host") + cls.t.run("create build_requires.py br_build/1.0@ --profile=build") + + def _check_user_info_data(self, app_scope, output): # Check information from the host context (using deps_user_info attribute) - self.assertIn("app/1.0: [deps] br_host, library", t.out) - self.assertIn("app/1.0: [deps] library.DATA=library-Windows", t.out) - self.assertIn("app/1.0: [deps] br_host.DATA=br_host-Windows", t.out) + self.assertIn(app_scope + ": [deps] br_host, library", output) + self.assertIn(app_scope + ": [deps] library.DATA=library-Windows", output) + self.assertIn(app_scope + ": [deps] br_host.DATA=br_host-Windows", output) # Check information from the build context (using user_info_build attribute) - self.assertIn("app/1.0: [build] br_build, library", t.out) - self.assertIn("app/1.0: [build] library.DATA=library-Linux", t.out) - self.assertIn("app/1.0: [build] br_build.DATA=br_build-Linux", t.out) + self.assertIn(app_scope + ": [build] br_build, library", output) + self.assertIn(app_scope + ": [build] library.DATA=library-Linux", output) + self.assertIn(app_scope + ": [build] br_build.DATA=br_build-Linux", output) + + def test_user_info_from_requirements(self): + self.t.run("create app.py app/1.0@ --profile:host=host --profile:build=build") + self._check_user_info_data("app/1.0", self.t.out) + + def test_user_info_local_workflow(self): + self.t.run("install app.py app/1.0@ --profile:host=host --profile:build=build") + self.t.run("build app.py") + self._check_user_info_data("app.py (app/1.0)", self.t.out) diff --git a/conans/test/functional/environment/apply_environment_test.py b/conans/test/functional/environment/apply_environment_test.py index 51a6e0f080a..e181c00ad6a 100644 --- a/conans/test/functional/environment/apply_environment_test.py +++ b/conans/test/functional/environment/apply_environment_test.py @@ -818,7 +818,7 @@ def load_conaninfo(lib): self.assertEqual(info.env_values.env_dicts("PROJECT"), ({'GLOBAL': '99'}, {'VAR3': ['newappend']})) - _, _, buildinfo = TXTGenerator.loads(client.load(BUILD_INFO)) + _, _, buildinfo, _ = TXTGenerator.loads(client.load(BUILD_INFO)) self.assertEqual(buildinfo["LIB_A"].VAR1, ["900"]) def _export(self, client, name, requires, env_vars, env_vars_append=None): diff --git a/conans/test/functional/graph/diamond_test.py b/conans/test/functional/graph/diamond_test.py index 1144993c061..69059bc7a98 100644 --- a/conans/test/functional/graph/diamond_test.py +++ b/conans/test/functional/graph/diamond_test.py @@ -47,7 +47,7 @@ def _check_individual_deps(self): self.assertIn("set(CONAN_LIBS helloHello3 helloHello1 helloHello2 helloHello0", cmakebuildinfo) self.assertIn("set(CONAN_DEPENDENCIES Hello3 Hello1 Hello2 Hello0)", cmakebuildinfo) - deps_cpp_info, _, _ = TXTGenerator.loads(content) + deps_cpp_info, _, _, _ = TXTGenerator.loads(content) self.assertEqual(len(deps_cpp_info.include_paths), 4) for dep in ("Hello3", "Hello2", "Hello1", "Hello0"): self.assertEqual(len(deps_cpp_info[dep].include_paths), 1) diff --git a/conans/test/unittests/client/generators/txt/test_content.py b/conans/test/unittests/client/generators/txt/test_content.py index 065f6724089..c710ead2dfe 100644 --- a/conans/test/unittests/client/generators/txt/test_content.py +++ b/conans/test/unittests/client/generators/txt/test_content.py @@ -7,6 +7,7 @@ from conans.model.env_info import EnvValues, EnvInfo from conans.model.ref import ConanFileReference from conans.model.settings import Settings +from conans.model.user_info import DepsUserInfo from conans.model.user_info import UserInfo from conans.test.utils.tools import TestBufferConanOutput @@ -90,3 +91,25 @@ def test_user_info(self): VAR1=value1 [USER_other-pkg] VAR1=other-value1"""), txt_out) + + def test_user_info_build(self): + conanfile = ConanFile(TestBufferConanOutput(), None) + conanfile.initialize(Settings({}), EnvValues()) + + conanfile.user_info_build = DepsUserInfo() + user_info = UserInfo() + user_info.VAR1 = "value1" + conanfile.user_info_build["build_pkg"] = user_info + + user_info = UserInfo() + user_info.VAR1 = "other-value1" + conanfile.user_info_build["other-build-pkg"] = user_info + + generator = TXTGenerator(conanfile) + txt_out = generator.content + + self.assertIn(textwrap.dedent(""" + [USERBUILD_build_pkg] + VAR1=value1 + [USERBUILD_other-build-pkg] + VAR1=other-value1"""), txt_out) diff --git a/conans/test/unittests/client/generators/txt/test_dump_load.py b/conans/test/unittests/client/generators/txt/test_dump_load.py index a48b9d03890..7b5b17c7e06 100644 --- a/conans/test/unittests/client/generators/txt/test_dump_load.py +++ b/conans/test/unittests/client/generators/txt/test_dump_load.py @@ -6,6 +6,7 @@ from conans.model.env_info import EnvValues, EnvInfo from conans.model.ref import ConanFileReference from conans.model.settings import Settings +from conans.model.user_info import DepsUserInfo from conans.model.user_info import UserInfo from conans.test.utils.tools import TestBufferConanOutput @@ -21,7 +22,7 @@ def test_names_per_generator(self): conanfile.initialize(Settings({}), EnvValues()) conanfile.deps_cpp_info.add("pkg_name", DepCppInfo(cpp_info)) content = TXTGenerator(conanfile).content - parsed_deps_cpp_info, _, _ = TXTGenerator.loads(content, filter_empty=False) + parsed_deps_cpp_info, _, _, _ = TXTGenerator.loads(content, filter_empty=False) parsed_cpp_info = parsed_deps_cpp_info["pkg_name"] # FIXME: Conan v2: Remove 'txt' generator or serialize all the names @@ -71,15 +72,26 @@ def test_idempotent(self): user_info.VAR1 = "other-value1" conanfile.deps_user_info["other-pkg"] = user_info + # Add user_info for BUILD + conanfile.user_info_build = DepsUserInfo() + user_info = UserInfo() + user_info.VAR1 = "value1" + conanfile.user_info_build["build_pkg"] = user_info + + user_info = UserInfo() + user_info.VAR1 = "other-value1" + conanfile.user_info_build["other-build-pkg"] = user_info + master_content = TXTGenerator(conanfile).content - after_cpp_info, after_user_info, after_env_info = TXTGenerator.loads(master_content, - filter_empty=False) + after_cpp_info, after_user_info, after_env_info, after_user_info_build = \ + TXTGenerator.loads(master_content, filter_empty=False) # Assign them to a different conanfile other_conanfile = ConanFile(TestBufferConanOutput(), None) other_conanfile.initialize(Settings({}), EnvValues()) other_conanfile.deps_cpp_info = after_cpp_info other_conanfile.deps_env_info = after_env_info other_conanfile.deps_user_info = after_user_info + other_conanfile.user_info_build = after_user_info_build after_content = TXTGenerator(other_conanfile).content self.assertListEqual(master_content.splitlines(), after_content.splitlines()) diff --git a/conans/test/unittests/client/generators/txt/test_load.py b/conans/test/unittests/client/generators/txt/test_load.py index 6861787351f..f7a55b9ff9b 100644 --- a/conans/test/unittests/client/generators/txt/test_load.py +++ b/conans/test/unittests/client/generators/txt/test_load.py @@ -31,10 +31,8 @@ def test_load_sytem_libs(self): requirement_other """) - deps_cpp_info, _, _ = TXTGenerator.loads(content) + deps_cpp_info, _, _, _ = TXTGenerator.loads(content) self.assertListEqual(list(deps_cpp_info.system_libs), ["requirement", "requirement_other"]) self.assertListEqual(list(deps_cpp_info["requirement"].system_libs), ["requirement", ]) self.assertListEqual(list(deps_cpp_info["requirement_other"].system_libs), ["requirement_other", ]) - - diff --git a/conans/test/unittests/client/tools/net_test.py b/conans/test/unittests/client/tools/net_test.py index 051a13624ec..3e42c74e01b 100644 --- a/conans/test/unittests/client/tools/net_test.py +++ b/conans/test/unittests/client/tools/net_test.py @@ -1,7 +1,6 @@ # coding=utf-8 import os -import platform import shutil import tempfile import unittest diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py index e3b5111b698..7f2d4775c40 100644 --- a/conans/test/unittests/model/build_info_test.py +++ b/conans/test/unittests/model/build_info_test.py @@ -56,7 +56,7 @@ def parse_test(self): [includedirs_My-Component-Tool] J:/my-component-tool """ - deps_cpp_info, _, _ = TXTGenerator.loads(text) + deps_cpp_info, _, _, _ = TXTGenerator.loads(text) def assert_cpp(deps_cpp_info_test): self.assertEqual(deps_cpp_info_test.includedirs, @@ -73,7 +73,7 @@ def assert_cpp(deps_cpp_info_test): [ENV_LIBA] VAR2=23 """ - deps_cpp_info, _, deps_env_info = TXTGenerator.loads(text2) + deps_cpp_info, _, deps_env_info, _ = TXTGenerator.loads(text2) assert_cpp(deps_cpp_info) self.assertEqual(deps_env_info["LIBA"].VAR2, "23") @@ -82,7 +82,7 @@ def assert_cpp(deps_cpp_info_test): [USER_LIBA] VAR2=23 """ - deps_cpp_info, deps_user_info, _ = TXTGenerator.loads(text3) + deps_cpp_info, deps_user_info, _, _ = TXTGenerator.loads(text3) assert_cpp(deps_cpp_info) self.assertEqual(deps_user_info["LIBA"].VAR2, "23") @@ -94,7 +94,7 @@ def assert_cpp(deps_cpp_info_test): [ENV_LIBA] VAR2=23 """ - deps_cpp_info, deps_user_info, deps_env_info = TXTGenerator.loads(text4) + deps_cpp_info, deps_user_info, deps_env_info, _ = TXTGenerator.loads(text4) assert_cpp(deps_cpp_info) self.assertEqual(deps_user_info["LIBA"].VAR2, "23") self.assertEqual(deps_env_info["LIBA"].VAR2, "23") @@ -111,7 +111,7 @@ def help_test(self): fakeconan = namedtuple("Conanfile", "deps_cpp_info cpp_info deps_env_info env_info user_info deps_user_info") output = TXTGenerator(fakeconan(deps_cpp_info, None, deps_env_info, None, {}, defaultdict(dict))).content - deps_cpp_info2, _, _ = TXTGenerator.loads(output) + deps_cpp_info2, _, _, _ = TXTGenerator.loads(output) self.assertEqual(deps_cpp_info.configs, deps_cpp_info2.configs) self.assertEqual(deps_cpp_info.includedirs, deps_cpp_info2.includedirs) self.assertEqual(deps_cpp_info.libdirs, deps_cpp_info2.libdirs) @@ -151,7 +151,7 @@ def configs_test(self): fakeconan = namedtuple("Conanfile", "deps_cpp_info cpp_info deps_env_info env_info user_info deps_user_info") output = TXTGenerator(fakeconan(deps_cpp_info, None, deps_env_info, deps_user_info, {}, defaultdict(dict))).content - deps_cpp_info2, _, deps_env_info2 = TXTGenerator.loads(output, filter_empty=False) + deps_cpp_info2, _, deps_env_info2, _ = TXTGenerator.loads(output, filter_empty=False) self.assertEqual(deps_cpp_info.includedirs, deps_cpp_info2.includedirs) self.assertEqual(deps_cpp_info.libdirs, deps_cpp_info2.libdirs) self.assertEqual(deps_cpp_info.bindirs, deps_cpp_info2.bindirs)