Skip to content

Commit

Permalink
[fix] Propagate 'user_info_build' using package development commands (#…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
jgsogo committed Aug 5, 2020
1 parent 448a701 commit e157492
Show file tree
Hide file tree
Showing 11 changed files with 128 additions and 64 deletions.
75 changes: 46 additions & 29 deletions conans/client/generators/text.py
Expand Up @@ -47,49 +47,59 @@ def __init__(self, cpp_info):

class TXTGenerator(Generator):
name = "txt"
_USER_INFO_HOST_PREFIX = "USER"
_USER_INFO_BUILD_PREFIX = "USERBUILD"

@property
def filename(self):
return BUILD_INFO

@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)
Expand Down Expand Up @@ -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)
5 changes: 4 additions & 1 deletion conans/client/graph/graph_manager.py
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions conans/model/conan_generator.py
Expand Up @@ -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):
Expand Down
49 changes: 30 additions & 19 deletions conans/test/functional/cross_building/user_info_test.py
Expand Up @@ -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)
Expand Up @@ -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):
Expand Down
2 changes: 1 addition & 1 deletion conans/test/functional/graph/diamond_test.py
Expand Up @@ -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)
Expand Down
23 changes: 23 additions & 0 deletions conans/test/unittests/client/generators/txt/test_content.py
Expand Up @@ -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

Expand Down Expand Up @@ -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)
18 changes: 15 additions & 3 deletions conans/test/unittests/client/generators/txt/test_dump_load.py
Expand Up @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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())
4 changes: 1 addition & 3 deletions conans/test/unittests/client/generators/txt/test_load.py
Expand Up @@ -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", ])


1 change: 0 additions & 1 deletion 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
Expand Down
12 changes: 6 additions & 6 deletions conans/test/unittests/model/build_info_test.py
Expand Up @@ -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,
Expand All @@ -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")

Expand All @@ -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")

Expand All @@ -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")
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit e157492

Please sign in to comment.