From e743bd242aae22223b42cc2ed9bf010543ff7eec Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Mon, 20 May 2019 18:28:43 +0200
Subject: [PATCH 01/47] Added tests and first POC component class with
 recursive libs retrieval

---
 conans/model/build_info.py                    | 69 +++++++++++++++++++
 .../test/unittests/model/build_info_test.py   | 41 ++++++++++-
 2 files changed, 109 insertions(+), 1 deletion(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index 5d31a09bf66..f1495ee9236 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -3,6 +3,8 @@
 
 import deprecation
 
+from conans.errors import ConanException
+
 DEFAULT_INCLUDE = "include"
 DEFAULT_LIB = "lib"
 DEFAULT_BIN = "bin"
@@ -16,6 +18,9 @@ class _CppInfo(object):
     specific systems will be produced from this info
     """
     def __init__(self):
+        self.name = None
+        self.exes = []
+        self.system_deps = []
         self.includedirs = []  # Ordered list of include paths
         self.srcdirs = []  # Ordered list of source paths
         self.libdirs = []  # Directories to find libraries
@@ -132,6 +137,70 @@ def _get_cpp_info():
         return self.configs.setdefault(config, _get_cpp_info())
 
 
+class Component(object):
+
+    def __init__(self, name):
+        self.name = name
+        self.deps = OrderedComponentsDict()
+        self._lib = None
+        self._exe = None
+        self.system_deps = []
+
+    @property
+    def libs(self):
+        return self.deps.items()
+
+    @property
+    def lib(self):
+        return self._lib
+
+    @lib.setter
+    def lib(self, name):
+        if self._exe:
+            raise ConanException("exe is already set")
+        self._lib = name
+
+    @property
+    def exe(self):
+        return self._exe
+
+    @exe.setter
+    def exe(self, name):
+        if self._lib:
+            raise ConanException("lib is already set")
+        self._exe = name
+
+
+class OrderedComponentsDict(object):
+
+    def __init__(self):
+        self._components = OrderedDict()
+
+    def __setitem__(self, key, value):
+        self._components[key] = value
+
+    def __getitem__(self, key):
+        if key not in self._components:
+            self._components[key] = Component(key)
+        return self._components[key]
+
+    def items(self):
+        values = []
+        for k, v in self._components.items():
+            items = self._components[k].deps.items()
+            if items:
+                values.extend(items)
+                if self._components[k].lib is not None:
+                    values.append(self._components[k].lib)
+            else:
+                values.append(self._components[k].lib)
+        ret = []
+        for v in values:
+            if v not in ret:
+                ret.append(v)
+        return ret
+
+
 class _BaseDepsCppInfo(_CppInfo):
     def __init__(self):
         super(_BaseDepsCppInfo, self).__init__()
diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index 631aac1a03d..6bae2e61ac3 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -3,7 +3,8 @@
 from collections import defaultdict, namedtuple
 
 from conans.client.generators import TXTGenerator
-from conans.model.build_info import CppInfo, DepsCppInfo
+from conans.errors import ConanException
+from conans.model.build_info import CppInfo, DepsCppInfo, Component
 from conans.model.env_info import DepsEnvInfo, EnvInfo
 from conans.model.user_info import DepsUserInfo
 from conans.test.utils.test_files import temp_folder
@@ -173,3 +174,41 @@ def cpp_info_test(self):
         self.assertEqual(info.lib_paths, [os.path.join(folder, "lib"), abs_lib])
         self.assertEqual(info.bin_paths, [abs_bin,
                                           os.path.join(folder, "local_bindir")])
+
+    def cpp_info_components_test(self):
+        folder = temp_folder()
+        info = CppInfo(folder)
+        self.assertIsNone(info.name)
+        self.assertEquals(info.exes, [])
+        self.assertEquals(info.system_deps, [])
+        info.libs
+
+    def basic_components_test(self):
+        component = Component("my_component")
+        self.assertIn(component.name, "my_component")
+        component.deps["hola"]
+        component.deps["hola"].lib = "libhola"
+        self.assertEquals(component.deps["hola"].lib, "libhola")
+        with self.assertRaisesRegexp(ConanException, "lib is already set"):
+            component.deps["hola"].exe = "hola.exe"
+        component.deps["hola"].lib = None
+        self.assertEquals(component.deps["hola"].lib, None)
+        component.deps["hola"].exe = "hola.exe"
+        self.assertEquals(component.deps["hola"].lib, None)
+        with self.assertRaisesRegexp(ConanException, "exe is already set"):
+            component.deps["hola"].lib = "libhola"
+
+    def nested_components_test(self):
+        component = Component("Greetings")
+        self.assertIn(component.name, "Greetings")
+        component.deps["greet"].exe = "greet.exe"
+        component.deps["greet"].deps["hola"].lib = "libhola"
+        component.deps["greet"].deps["adios"].lib = "libadios"
+        component.deps["greet"].deps["hola"].deps["say"].lib = "libsay"
+        component.deps["greet"].deps["adios"].deps["say"].lib = "libsay"
+        component.deps["greet"].deps["hru"].lib = "libhru"
+        self.assertEquals(component.libs, ["libsay", "libhola", "libadios", "libhru"])
+        self.assertEquals(component.deps["greet"].deps["hru"].libs, [])
+        self.assertEquals(component.deps["greet"].deps["hola"].libs, ["libsay"])
+        self.assertEquals(component.deps["greet"].deps["adios"].libs, ["libsay"])
+        self.assertEquals(component.deps["greet"].deps["adios"].deps["say"].libs, [])
\ No newline at end of file

From 7073291cf9555bc4c74bbc4245dc62acba80dce9 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Tue, 21 May 2019 12:10:25 +0200
Subject: [PATCH 02/47] Removed ordered dict class and moved to component class

---
 conans/model/build_info.py                    | 83 ++++++++++++-------
 .../test/unittests/model/build_info_test.py   | 69 ++++++++-------
 2 files changed, 92 insertions(+), 60 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index f1495ee9236..a24f8f84065 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -120,6 +120,19 @@ def __init__(self, root_folder):
         # public_deps is needed to accumulate list of deps for cmake targets
         self.public_deps = []
         self.configs = {}
+        self._deps = OrderedDict()
+
+    @property
+    def deps(self):
+        return self._deps.keys()
+
+    def __setitem__(self, key, value):
+        self._deps[key] = value
+
+    def __getitem__(self, key):
+        if key not in self.deps:
+            self._deps[key] = Component(key)
+        return self._deps[key]
 
     def __getattr__(self, config):
 
@@ -141,14 +154,50 @@ class Component(object):
 
     def __init__(self, name):
         self.name = name
-        self.deps = OrderedComponentsDict()
+        self._deps = OrderedDict()
         self._lib = None
         self._exe = None
         self.system_deps = []
+        self.includedirs = []
+        self.libdirs = []
+        self.resdirs = []
+        self.bindirs = []
+        self.builddirs = []
+        self.defines = []
+        self.cflags = []
+        self.cppflags = []
+        self.cxxflags = []
+        self.sharedlinkflags = []
+        self.exelinkflags = []
+
+    @property
+    def deps(self):
+        return self._deps.keys()
+
+    def __setitem__(self, key, value):
+        self._deps[key] = value
+
+    def __getitem__(self, key):
+        if key not in self._deps:
+            self._deps[key] = Component(key)
+        return self._deps[key]
 
     @property
     def libs(self):
-        return self.deps.items()
+        values = []
+        for k, v in self._deps.items():
+            items = self._deps[k]._deps.items()
+            if items:
+                values.extend(self._deps[k].libs)
+                if self._deps[k].lib is not None:
+                    values.append(self._deps[k].lib)
+            else:
+                values.append(self._deps[k].lib)
+        ret = []
+        for v in values:
+            if v not in ret:
+                ret.append(v)
+        return ret
 
     @property
     def lib(self):
@@ -171,36 +220,6 @@ def exe(self, name):
         self._exe = name
 
 
-class OrderedComponentsDict(object):
-
-    def __init__(self):
-        self._components = OrderedDict()
-
-    def __setitem__(self, key, value):
-        self._components[key] = value
-
-    def __getitem__(self, key):
-        if key not in self._components:
-            self._components[key] = Component(key)
-        return self._components[key]
-
-    def items(self):
-        values = []
-        for k, v in self._components.items():
-            items = self._components[k].deps.items()
-            if items:
-                values.extend(items)
-                if self._components[k].lib is not None:
-                    values.append(self._components[k].lib)
-            else:
-                values.append(self._components[k].lib)
-        ret = []
-        for v in values:
-            if v not in ret:
-                ret.append(v)
-        return ret
-
-
 class _BaseDepsCppInfo(_CppInfo):
     def __init__(self):
         super(_BaseDepsCppInfo, self).__init__()
diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index 6bae2e61ac3..321cbd1fc40 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -175,40 +175,53 @@ def cpp_info_test(self):
         self.assertEqual(info.bin_paths, [abs_bin,
                                           os.path.join(folder, "local_bindir")])
 
-    def cpp_info_components_test(self):
-        folder = temp_folder()
-        info = CppInfo(folder)
-        self.assertIsNone(info.name)
-        self.assertEquals(info.exes, [])
-        self.assertEquals(info.system_deps, [])
-        info.libs
-
     def basic_components_test(self):
         component = Component("my_component")
         self.assertIn(component.name, "my_component")
-        component.deps["hola"]
-        component.deps["hola"].lib = "libhola"
-        self.assertEquals(component.deps["hola"].lib, "libhola")
+        component["hola"]
+        component["hola"].lib = "libhola"
+        self.assertEquals(component["hola"].lib, "libhola")
         with self.assertRaisesRegexp(ConanException, "lib is already set"):
-            component.deps["hola"].exe = "hola.exe"
-        component.deps["hola"].lib = None
-        self.assertEquals(component.deps["hola"].lib, None)
-        component.deps["hola"].exe = "hola.exe"
-        self.assertEquals(component.deps["hola"].lib, None)
+            component["hola"].exe = "hola.exe"
+        component["hola"].lib = None
+        self.assertEquals(component["hola"].lib, None)
+        component["hola"].exe = "hola.exe"
+        self.assertEquals(component["hola"].lib, None)
         with self.assertRaisesRegexp(ConanException, "exe is already set"):
-            component.deps["hola"].lib = "libhola"
+            component["hola"].lib = "libhola"
 
-    def nested_components_test(self):
+    def nested_components_libs_order_test(self):
         component = Component("Greetings")
         self.assertIn(component.name, "Greetings")
-        component.deps["greet"].exe = "greet.exe"
-        component.deps["greet"].deps["hola"].lib = "libhola"
-        component.deps["greet"].deps["adios"].lib = "libadios"
-        component.deps["greet"].deps["hola"].deps["say"].lib = "libsay"
-        component.deps["greet"].deps["adios"].deps["say"].lib = "libsay"
-        component.deps["greet"].deps["hru"].lib = "libhru"
+        component["greet"].exe = "greet.exe"
+        component["greet"]["hola"].lib = "libhola"
+        component["greet"]["adios"].lib = "libadios"
+        component["greet"]["hola"]["say"].lib = "libsay"
+        component["greet"]["adios"]["say"].lib = "libsay"
+        component["greet"]["hru"].lib = "libhru"
         self.assertEquals(component.libs, ["libsay", "libhola", "libadios", "libhru"])
-        self.assertEquals(component.deps["greet"].deps["hru"].libs, [])
-        self.assertEquals(component.deps["greet"].deps["hola"].libs, ["libsay"])
-        self.assertEquals(component.deps["greet"].deps["adios"].libs, ["libsay"])
-        self.assertEquals(component.deps["greet"].deps["adios"].deps["say"].libs, [])
\ No newline at end of file
+        self.assertEquals(component["greet"]["hru"].libs, [])
+        self.assertEquals(component["greet"]["hola"].libs, ["libsay"])
+        self.assertEquals(component["greet"]["adios"].libs, ["libsay"])
+        self.assertEquals(component["greet"]["adios"]["say"].libs, [])
+
+    def components_deps_test(self):
+        component = Component("OpenSSL")
+        component["OpenSSL"]["Crypto"]["SSL"]
+        component["OpenSSL"]["Other"]
+        component["Another"]
+        self.assertEqual(component.deps, ["OpenSSL", "Another"])
+        self.assertEqual(component["OpenSSL"].deps, ["Crypto", "Other"])
+        self.assertEqual(component["OpenSSL"]["Crypto"].deps, ["SSL"])
+        self.assertEqual(component["Another"].deps, [])
+        self.assertEqual(component["OpenSSL"]["Other"].deps, [])
+        self.assertEqual(component["OpenSSL"]["Crypto"]["SSL"].deps, [])
+
+    def cppinfo_components_test(self):
+        folder = temp_folder()
+        info = CppInfo(folder)
+        info.name = "OpenSSL"
+        info["OpenSSL"].includedirs = ["include"]
+        info["OpenSSL"]["Crypto"].includedirs = ["headers"]
+        self.assertEqual(info["OpenSSL"].includedirs, ["include"])
+        self.assertEqual(info["OpenSSL"]["Crypto"].includedirs, ["headers"])

From 66289a7fdeb1f5d9dedc2baa2a10c39e69aba2e9 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Tue, 21 May 2019 13:58:51 +0200
Subject: [PATCH 03/47] Propagate lib and exe to cpp_info and maintain current
 behavior or libs

---
 conans/model/build_info.py                    | 101 +++++++++++++++++-
 .../test/unittests/model/build_info_test.py   |  55 ++++++++++
 2 files changed, 152 insertions(+), 4 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index a24f8f84065..1592b5dee6b 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -19,7 +19,6 @@ class _CppInfo(object):
     """
     def __init__(self):
         self.name = None
-        self.exes = []
         self.system_deps = []
         self.includedirs = []  # Ordered list of include paths
         self.srcdirs = []  # Ordered list of source paths
@@ -28,7 +27,7 @@ def __init__(self):
         self.bindirs = []  # Directories to find executables and shared libs
         self.builddirs = []
         self.rootpaths = []
-        self.libs = []  # The libs to link against
+        self._libs = []  # The libs to link against
         self.defines = []  # preprocessor definitions
         self.cflags = []  # pure C flags
         self.cxxflags = []  # C++ compilation flags
@@ -121,15 +120,90 @@ def __init__(self, root_folder):
         self.public_deps = []
         self.configs = {}
         self._deps = OrderedDict()
+        self._lib = None
+        self._exe = None
 
     @property
     def deps(self):
         return self._deps.keys()
 
+    @property
+    def lib(self):
+        return self._lib
+
+    @lib.setter
+    def lib(self, name):
+        if self._exe:
+            raise ConanException("exe is already set")
+        if self._deps:
+            raise ConanException("Setting a first level lib is not supported when Components are "
+                                 "already in use")
+        self._lib = name
+
+    @property
+    def libs(self):
+        if self._deps:
+            values = []
+            for k, v in self._deps.items():
+                if self._deps[k]._deps.items():
+                    values.extend(self._deps[k].libs)
+                    if self._deps[k].lib is not None:
+                        values.append(self._deps[k].lib)
+                else:
+                    values.append(self._deps[k].lib)
+            ret = []
+            for v in values:
+                if v not in ret:
+                    ret.append(v)
+            return ret
+        else:
+            return self._libs
+
+    @libs.setter
+    def libs(self, libs):
+        if self._deps:
+            raise ConanException("Setting a first level libs is not supported when Components are "
+                                 "already in use")
+        self._libs = libs
+
+    @property
+    def exe(self):
+        return self._exe
+
+    @exe.setter
+    def exe(self, name):
+        if self._lib:
+            raise ConanException("lib is already set")
+        if self._deps:
+            raise ConanException("Setting a first level exe is not supported when Components are "
+                                 "already in use")
+        self._exe = name
+
+    @property
+    def exes(self):
+        if self._deps:
+            values = []
+            for k, v in self._deps.items():
+                if self._deps[k]._deps.items():
+                    if self._deps[k].exe is not None:
+                        values.append(self._deps[k].exe)
+                    values.extend(self._deps[k].exes)
+                else:
+                    values.append(self._deps[k].exe)
+            return values
+        else:
+            return [self.exe]
+
     def __setitem__(self, key, value):
+        if self._exe or self._lib:
+            raise ConanException("Usage of Components with 'exe' or 'lib' values is no allowed")
+        if self._libs or self._lib:
+            raise ConanException("Usage of Components with 'libs' values is no allowed")
         self._deps[key] = value
 
     def __getitem__(self, key):
+        if self._exe or self._lib:
+            raise ConanException("Usage of Components with 'exe' or 'lib' values is no allowed")
         if key not in self.deps:
             self._deps[key] = Component(key)
         return self._deps[key]
@@ -186,8 +260,7 @@ def __getitem__(self, key):
     def libs(self):
         values = []
         for k, v in self._deps.items():
-            items = self._deps[k]._deps.items()
-            if items:
+            if self._deps[k]._deps.items():
                 values.extend(self._deps[k].libs)
                 if self._deps[k].lib is not None:
                     values.append(self._deps[k].lib)
@@ -209,6 +282,18 @@ def lib(self, name):
             raise ConanException("exe is already set")
         self._lib = name
 
+    @property
+    def exes(self):
+        values = []
+        for k, v in self._deps.items():
+            if self._deps[k]._deps.items():
+                if self._deps[k].exe is not None:
+                    values.append(self._deps[k].exe)
+                values.extend(self._deps[k].exes)
+            else:
+                values.append(self._deps[k].exe)
+        return values
+
     @property
     def exe(self):
         return self._exe
@@ -248,6 +333,14 @@ def merge_lists(seq1, seq2):
         if not self.sysroot:
             self.sysroot = dep_cpp_info.sysroot
 
+    @property
+    def libs(self):
+        return self._libs
+
+    @libs.setter
+    def libs(self, libs):
+        self._libs = libs
+
     @property
     def include_paths(self):
         return self.includedirs
diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index 321cbd1fc40..df44d6bf631 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -205,6 +205,61 @@ def nested_components_libs_order_test(self):
         self.assertEquals(component["greet"]["adios"].libs, ["libsay"])
         self.assertEquals(component["greet"]["adios"]["say"].libs, [])
 
+    def cpp_info_nested_components_libs_order_test(self):
+        info = CppInfo(None)
+        info.name = "Greetings"
+        self.assertIn(info.name, "Greetings")
+        info["greet"].exe = "greet.exe"
+        info["greet"]["hola"].lib = "libhola"
+        info["greet"]["adios"].lib = "libadios"
+        info["greet"]["hola"]["say"].lib = "libsay"
+        info["greet"]["adios"]["say"].lib = "libsay"
+        info["greet"]["hru"].lib = "libhru"
+        self.assertEquals(info.libs, ["libsay", "libhola", "libadios", "libhru"])
+        self.assertEquals(info["greet"]["hru"].libs, [])
+        self.assertEquals(info["greet"]["hola"].libs, ["libsay"])
+        self.assertEquals(info["greet"]["adios"].libs, ["libsay"])
+        self.assertEquals(info["greet"]["adios"]["say"].libs, [])
+
+    def cpp_info_exes_test(self):
+        info = CppInfo(None)
+        info.name = "Greetings"
+        self.assertIn(info.name, "Greetings")
+        info["greet"].exe = "greet.exe"
+        info["hola"].exe = "hola.exe"
+        info["adios"].exe = "adios.exe"
+        info["say"].exe = "say.exe"
+        info["hru"].exe = "hru.exe"
+        self.assertEqual(["greet.exe", "hola.exe", "adios.exe", "say.exe", "hru.exe"], info.exes)
+
+    def cpp_info_nested_exes_test(self):
+        info = CppInfo(None)
+        info.name = "Greetings"
+        self.assertIn(info.name, "Greetings")
+        info["greet"].exe = "greet.exe"
+        info["greet"]["hola"].exe = "hola.exe"
+        info["greet"]["hola"]["say"].exe = "say.exe"
+        info["greet"]["adios"].exe = "adios.exe"
+        info["hru"].exe = "hru.exe"
+        self.assertEqual(["greet.exe", "hola.exe", "say.exe", "adios.exe", "hru.exe"], info.exes)
+
+    def cpp_info_exes_components_fail_test(self):
+        """
+        Usage of .lib or .exe is not allowed in cpp_info when using components
+        """
+        info = CppInfo(None)
+        info.name = "Greetings"
+        self.assertIn(info.name, "Greetings")
+        info.exe = "greet.exe"
+        with self.assertRaisesRegexp(ConanException, "Usage of Components with 'exe' or 'lib' "
+                                                     "values is no allowed"):
+            info["hola"].exe = "hola.exe"
+        info.exe = None
+        info["hola"].exe = "hola.exe"
+        with self.assertRaisesRegexp(ConanException, "Setting a first level exe is not supported "
+                                                     "when Components are already in use"):
+            info.exe = "greet.exe"
+
     def components_deps_test(self):
         component = Component("OpenSSL")
         component["OpenSSL"]["Crypto"]["SSL"]

From 07f90a730d1daa7d2c66cdab9a90ebf6f9049924 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Tue, 21 May 2019 16:48:19 +0200
Subject: [PATCH 04/47] Removed multilevel components and recursivity

---
 conans/model/build_info.py                    | 120 ++----------------
 .../test/unittests/model/build_info_test.py   | 104 +++------------
 2 files changed, 27 insertions(+), 197 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index 1592b5dee6b..7970035be0f 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -120,91 +120,27 @@ def __init__(self, root_folder):
         self.public_deps = []
         self.configs = {}
         self._deps = OrderedDict()
-        self._lib = None
-        self._exe = None
-
-    @property
-    def deps(self):
-        return self._deps.keys()
-
-    @property
-    def lib(self):
-        return self._lib
-
-    @lib.setter
-    def lib(self, name):
-        if self._exe:
-            raise ConanException("exe is already set")
-        if self._deps:
-            raise ConanException("Setting a first level lib is not supported when Components are "
-                                 "already in use")
-        self._lib = name
 
     @property
     def libs(self):
         if self._deps:
-            values = []
-            for k, v in self._deps.items():
-                if self._deps[k]._deps.items():
-                    values.extend(self._deps[k].libs)
-                    if self._deps[k].lib is not None:
-                        values.append(self._deps[k].lib)
-                else:
-                    values.append(self._deps[k].lib)
-            ret = []
-            for v in values:
-                if v not in ret:
-                    ret.append(v)
-            return ret
+            deps = [v for v in self._deps.values()]
+            deps_sorted = sorted(deps, key=lambda component: len(component.deps))
+            return [dep.lib for dep in deps_sorted if dep.lib is not None]
         else:
             return self._libs
 
     @libs.setter
     def libs(self, libs):
         if self._deps:
-            raise ConanException("Setting a first level libs is not supported when Components are "
+            raise ConanException("Setting first level libs is not supported when Components are "
                                  "already in use")
         self._libs = libs
 
-    @property
-    def exe(self):
-        return self._exe
-
-    @exe.setter
-    def exe(self, name):
-        if self._lib:
-            raise ConanException("lib is already set")
-        if self._deps:
-            raise ConanException("Setting a first level exe is not supported when Components are "
-                                 "already in use")
-        self._exe = name
-
-    @property
-    def exes(self):
-        if self._deps:
-            values = []
-            for k, v in self._deps.items():
-                if self._deps[k]._deps.items():
-                    if self._deps[k].exe is not None:
-                        values.append(self._deps[k].exe)
-                    values.extend(self._deps[k].exes)
-                else:
-                    values.append(self._deps[k].exe)
-            return values
-        else:
-            return [self.exe]
-
-    def __setitem__(self, key, value):
-        if self._exe or self._lib:
-            raise ConanException("Usage of Components with 'exe' or 'lib' values is no allowed")
-        if self._libs or self._lib:
-            raise ConanException("Usage of Components with 'libs' values is no allowed")
-        self._deps[key] = value
-
     def __getitem__(self, key):
-        if self._exe or self._lib:
-            raise ConanException("Usage of Components with 'exe' or 'lib' values is no allowed")
-        if key not in self.deps:
+        if self._libs:
+            raise ConanException("Usage of Components with '.libs' values is not allowed")
+        if key not in self._deps.keys():
             self._deps[key] = Component(key)
         return self._deps[key]
 
@@ -228,7 +164,7 @@ class Component(object):
 
     def __init__(self, name):
         self.name = name
-        self._deps = OrderedDict()
+        self.deps = []
         self._lib = None
         self._exe = None
         self.system_deps = []
@@ -244,34 +180,6 @@ def __init__(self, name):
         self.sharedlinkflags = []
         self.exelinkflags = []
 
-    @property
-    def deps(self):
-        return self._deps.keys()
-
-    def __setitem__(self, key, value):
-        self._deps[key] = value
-
-    def __getitem__(self, key):
-        if key not in self._deps:
-            self._deps[key] = Component(key)
-        return self._deps[key]
-
-    @property
-    def libs(self):
-        values = []
-        for k, v in self._deps.items():
-            if self._deps[k]._deps.items():
-                values.extend(self._deps[k].libs)
-                if self._deps[k].lib is not None:
-                    values.append(self._deps[k].lib)
-            else:
-                values.append(self._deps[k].lib)
-        ret = []
-        for v in values:
-            if v not in ret:
-                ret.append(v)
-        return ret
-
     @property
     def lib(self):
         return self._lib
@@ -282,18 +190,6 @@ def lib(self, name):
             raise ConanException("exe is already set")
         self._lib = name
 
-    @property
-    def exes(self):
-        values = []
-        for k, v in self._deps.items():
-            if self._deps[k]._deps.items():
-                if self._deps[k].exe is not None:
-                    values.append(self._deps[k].exe)
-                values.extend(self._deps[k].exes)
-            else:
-                values.append(self._deps[k].exe)
-        return values
-
     @property
     def exe(self):
         return self._exe
diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index df44d6bf631..e76add10467 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -178,105 +178,39 @@ def cpp_info_test(self):
     def basic_components_test(self):
         component = Component("my_component")
         self.assertIn(component.name, "my_component")
-        component["hola"]
-        component["hola"].lib = "libhola"
-        self.assertEquals(component["hola"].lib, "libhola")
+        component.lib = "libhola"
+        self.assertEquals(component.lib, "libhola")
         with self.assertRaisesRegexp(ConanException, "lib is already set"):
-            component["hola"].exe = "hola.exe"
-        component["hola"].lib = None
-        self.assertEquals(component["hola"].lib, None)
-        component["hola"].exe = "hola.exe"
-        self.assertEquals(component["hola"].lib, None)
+            component.exe = "hola.exe"
+        component.lib = None
+        component.exe = "hola.exe"
+        self.assertEquals(component.lib, None)
         with self.assertRaisesRegexp(ConanException, "exe is already set"):
-            component["hola"].lib = "libhola"
+            component.lib = "libhola"
 
-    def nested_components_libs_order_test(self):
-        component = Component("Greetings")
-        self.assertIn(component.name, "Greetings")
-        component["greet"].exe = "greet.exe"
-        component["greet"]["hola"].lib = "libhola"
-        component["greet"]["adios"].lib = "libadios"
-        component["greet"]["hola"]["say"].lib = "libsay"
-        component["greet"]["adios"]["say"].lib = "libsay"
-        component["greet"]["hru"].lib = "libhru"
-        self.assertEquals(component.libs, ["libsay", "libhola", "libadios", "libhru"])
-        self.assertEquals(component["greet"]["hru"].libs, [])
-        self.assertEquals(component["greet"]["hola"].libs, ["libsay"])
-        self.assertEquals(component["greet"]["adios"].libs, ["libsay"])
-        self.assertEquals(component["greet"]["adios"]["say"].libs, [])
-
-    def cpp_info_nested_components_libs_order_test(self):
-        info = CppInfo(None)
-        info.name = "Greetings"
-        self.assertIn(info.name, "Greetings")
-        info["greet"].exe = "greet.exe"
-        info["greet"]["hola"].lib = "libhola"
-        info["greet"]["adios"].lib = "libadios"
-        info["greet"]["hola"]["say"].lib = "libsay"
-        info["greet"]["adios"]["say"].lib = "libsay"
-        info["greet"]["hru"].lib = "libhru"
-        self.assertEquals(info.libs, ["libsay", "libhola", "libadios", "libhru"])
-        self.assertEquals(info["greet"]["hru"].libs, [])
-        self.assertEquals(info["greet"]["hola"].libs, ["libsay"])
-        self.assertEquals(info["greet"]["adios"].libs, ["libsay"])
-        self.assertEquals(info["greet"]["adios"]["say"].libs, [])
-
-    def cpp_info_exes_test(self):
-        info = CppInfo(None)
-        info.name = "Greetings"
-        self.assertIn(info.name, "Greetings")
-        info["greet"].exe = "greet.exe"
-        info["hola"].exe = "hola.exe"
-        info["adios"].exe = "adios.exe"
-        info["say"].exe = "say.exe"
-        info["hru"].exe = "hru.exe"
-        self.assertEqual(["greet.exe", "hola.exe", "adios.exe", "say.exe", "hru.exe"], info.exes)
-
-    def cpp_info_nested_exes_test(self):
-        info = CppInfo(None)
-        info.name = "Greetings"
-        self.assertIn(info.name, "Greetings")
-        info["greet"].exe = "greet.exe"
-        info["greet"]["hola"].exe = "hola.exe"
-        info["greet"]["hola"]["say"].exe = "say.exe"
-        info["greet"]["adios"].exe = "adios.exe"
-        info["hru"].exe = "hru.exe"
-        self.assertEqual(["greet.exe", "hola.exe", "say.exe", "adios.exe", "hru.exe"], info.exes)
-
-    def cpp_info_exes_components_fail_test(self):
+    def cpp_info_libs_components_fail_test(self):
         """
-        Usage of .lib or .exe is not allowed in cpp_info when using components
+        Usage of .libs is not allowed in cpp_info when using Components
         """
         info = CppInfo(None)
         info.name = "Greetings"
         self.assertIn(info.name, "Greetings")
-        info.exe = "greet.exe"
-        with self.assertRaisesRegexp(ConanException, "Usage of Components with 'exe' or 'lib' "
-                                                     "values is no allowed"):
+        info.libs = ["libgreet"]
+        with self.assertRaisesRegexp(ConanException, "Usage of Components with '.libs' values is "
+                                                     "not allowed"):
             info["hola"].exe = "hola.exe"
-        info.exe = None
-        info["hola"].exe = "hola.exe"
-        with self.assertRaisesRegexp(ConanException, "Setting a first level exe is not supported "
-                                                     "when Components are already in use"):
-            info.exe = "greet.exe"
 
-    def components_deps_test(self):
-        component = Component("OpenSSL")
-        component["OpenSSL"]["Crypto"]["SSL"]
-        component["OpenSSL"]["Other"]
-        component["Another"]
-        self.assertEqual(component.deps, ["OpenSSL", "Another"])
-        self.assertEqual(component["OpenSSL"].deps, ["Crypto", "Other"])
-        self.assertEqual(component["OpenSSL"]["Crypto"].deps, ["SSL"])
-        self.assertEqual(component["Another"].deps, [])
-        self.assertEqual(component["OpenSSL"]["Other"].deps, [])
-        self.assertEqual(component["OpenSSL"]["Crypto"]["SSL"].deps, [])
+        info.libs = []
+        info["greet"].exe = "libgreet"
+        with self.assertRaisesRegexp(ConanException, "Setting first level libs is not supported "
+                                                     "when Components are already in use"):
+            info.libs = ["libgreet"]
 
     def cppinfo_components_test(self):
         folder = temp_folder()
         info = CppInfo(folder)
         info.name = "OpenSSL"
         info["OpenSSL"].includedirs = ["include"]
-        info["OpenSSL"]["Crypto"].includedirs = ["headers"]
+        info["Crypto"].includedirs = ["headers"]
         self.assertEqual(info["OpenSSL"].includedirs, ["include"])
-        self.assertEqual(info["OpenSSL"]["Crypto"].includedirs, ["headers"])
+        self.assertEqual(info["Crypto"].includedirs, ["headers"])

From 25a27764c2f3893109e2ed41dc88e0a12b3ffe64 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Tue, 21 May 2019 17:59:24 +0200
Subject: [PATCH 05/47] implemented inheritance of includedir directory

---
 conans/model/build_info.py                    | 20 +++++++++++----
 .../test/unittests/model/build_info_test.py   | 25 ++++++++++++++-----
 2 files changed, 34 insertions(+), 11 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index 7970035be0f..c06e0d94729 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -141,7 +141,7 @@ def __getitem__(self, key):
         if self._libs:
             raise ConanException("Usage of Components with '.libs' values is not allowed")
         if key not in self._deps.keys():
-            self._deps[key] = Component(key)
+            self._deps[key] = Component(self, key)
         return self._deps[key]
 
     def __getattr__(self, config):
@@ -162,13 +162,14 @@ def _get_cpp_info():
 
 class Component(object):
 
-    def __init__(self, name):
+    def __init__(self, parent, name):
+        self._parent = parent
         self.name = name
         self.deps = []
         self._lib = None
         self._exe = None
         self.system_deps = []
-        self.includedirs = []
+        self._includedirs = []
         self.libdirs = []
         self.resdirs = []
         self.bindirs = []
@@ -187,7 +188,7 @@ def lib(self):
     @lib.setter
     def lib(self, name):
         if self._exe:
-            raise ConanException("exe is already set")
+            raise ConanException("'.exe' is already set for this Component")
         self._lib = name
 
     @property
@@ -197,9 +198,18 @@ def exe(self):
     @exe.setter
     def exe(self, name):
         if self._lib:
-            raise ConanException("lib is already set")
+            raise ConanException("'.lib' is already set for this Component")
         self._exe = name
 
+    @property
+    def includedirs(self):
+        includedirs = self._parent.includedirs + self._includedirs
+        return list(OrderedDict.fromkeys(includedirs))
+
+    @includedirs.setter
+    def includedirs(self, value):
+        self._includedirs = value
+
 
 class _BaseDepsCppInfo(_CppInfo):
     def __init__(self):
diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index e76add10467..0c123aa24ac 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -176,16 +176,17 @@ def cpp_info_test(self):
                                           os.path.join(folder, "local_bindir")])
 
     def basic_components_test(self):
-        component = Component("my_component")
+        cpp_info = CppInfo(None)
+        component = cpp_info["my_component"]
         self.assertIn(component.name, "my_component")
         component.lib = "libhola"
         self.assertEquals(component.lib, "libhola")
-        with self.assertRaisesRegexp(ConanException, "lib is already set"):
+        with self.assertRaisesRegexp(ConanException, "'.lib' is already set for this Component"):
             component.exe = "hola.exe"
         component.lib = None
         component.exe = "hola.exe"
         self.assertEquals(component.lib, None)
-        with self.assertRaisesRegexp(ConanException, "exe is already set"):
+        with self.assertRaisesRegexp(ConanException, "'.exe' is already set for this Component"):
             component.lib = "libhola"
 
     def cpp_info_libs_components_fail_test(self):
@@ -206,11 +207,23 @@ def cpp_info_libs_components_fail_test(self):
                                                      "when Components are already in use"):
             info.libs = ["libgreet"]
 
-    def cppinfo_components_test(self):
+    def cppinfo_includedirs_test(self):
         folder = temp_folder()
         info = CppInfo(folder)
         info.name = "OpenSSL"
         info["OpenSSL"].includedirs = ["include"]
         info["Crypto"].includedirs = ["headers"]
-        self.assertEqual(info["OpenSSL"].includedirs, ["include"])
-        self.assertEqual(info["Crypto"].includedirs, ["headers"])
+        self.assertEqual(["include"], info["OpenSSL"].includedirs)
+        self.assertEqual(["include", "headers"], info["Crypto"].includedirs)
+
+        info.includedirs = ["my_headers"]
+        self.assertEqual(["my_headers", "include"], info["OpenSSL"].includedirs)
+        self.assertEqual(["my_headers", "headers"], info["Crypto"].includedirs)
+
+        info["Crypto"].includedirs = ["different_include"]
+        self.assertEqual(["my_headers", "different_include"], info["Crypto"].includedirs)
+
+        info["Crypto"].includedirs.extend(["another_include"])
+        # FIXME:
+        # self.assertEqual(["my_headers", "different_include", "another_include"],
+        #                 info["Crypto"].includedirs)

From a100bc26eff135ddc9ef633e7303fea2d6b2e12f Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Wed, 22 May 2019 11:17:15 +0200
Subject: [PATCH 06/47] comment

---
 conans/test/unittests/model/build_info_test.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index 0c123aa24ac..16398d21c48 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -223,7 +223,7 @@ def cppinfo_includedirs_test(self):
         info["Crypto"].includedirs = ["different_include"]
         self.assertEqual(["my_headers", "different_include"], info["Crypto"].includedirs)
 
-        info["Crypto"].includedirs.extend(["another_include"])
         # FIXME:
+        # info["Crypto"].includedirs.extend(["another_include"])
         # self.assertEqual(["my_headers", "different_include", "another_include"],
         #                 info["Crypto"].includedirs)

From e43b44268a37af578d9b6950c4bb2e254191f35c Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Tue, 28 May 2019 13:36:39 +0200
Subject: [PATCH 07/47] Directory propagation and integration test

---
 conans/model/build_info.py                    | 44 ++++++++++++++++--
 conans/test/integration/package_info_test.py  | 46 +++++++++++++++++++
 .../test/unittests/model/build_info_test.py   | 34 +++++++++++++-
 3 files changed, 119 insertions(+), 5 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index c06e0d94729..5772525f40a 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -170,10 +170,10 @@ def __init__(self, parent, name):
         self._exe = None
         self.system_deps = []
         self._includedirs = []
-        self.libdirs = []
-        self.resdirs = []
-        self.bindirs = []
-        self.builddirs = []
+        self._libdirs = []
+        self._resdirs = []
+        self._bindirs = []
+        self._builddirs = []
         self.defines = []
         self.cflags = []
         self.cppflags = []
@@ -210,6 +210,42 @@ def includedirs(self):
     def includedirs(self, value):
         self._includedirs = value
 
+    @property
+    def libdirs(self):
+        libdirs = self._parent.libdirs + self._libdirs
+        return list(OrderedDict.fromkeys(libdirs))
+
+    @libdirs.setter
+    def libdirs(self, value):
+        self._libdirs = value
+
+    @property
+    def resdirs(self):
+        resdirs = self._parent.resdirs + self._resdirs
+        return list(OrderedDict.fromkeys(resdirs))
+
+    @resdirs.setter
+    def resdirs(self, value):
+        self._resdirs = value
+
+    @property
+    def bindirs(self):
+        bindirs = self._parent.bindirs + self._bindirs
+        return list(OrderedDict.fromkeys(bindirs))
+
+    @bindirs.setter
+    def bindirs(self, value):
+        self._bindirs = value
+
+    @property
+    def builddirs(self):
+        builddirs = self._parent.builddirs + self._builddirs
+        return list(OrderedDict.fromkeys(builddirs))
+
+    @builddirs.setter
+    def builddirs(self, value):
+        self._builddirs = value
+
 
 class _BaseDepsCppInfo(_CppInfo):
     def __init__(self):
diff --git a/conans/test/integration/package_info_test.py b/conans/test/integration/package_info_test.py
index dad8b6ad4f0..22f6d0c3f7d 100644
--- a/conans/test/integration/package_info_test.py
+++ b/conans/test/integration/package_info_test.py
@@ -1,3 +1,4 @@
+import textwrap
 import unittest
 
 from conans.paths import CONANFILE, CONANFILE_TXT
@@ -46,3 +47,48 @@ def package_info(self):
 
         client.run("install . -o *:switch=0 --build Lib3")
         self.assertIn("Lib3/1.0@conan/stable: WARN: Env var MYVAR=foo", client.out)
+
+    def package_info_components_test(self):
+        dep = textwrap.dedent("""
+        from conans import ConanFile
+
+        class Dep(ConanFile):
+
+            def package_info(self):
+                self.cpp_info.name = "Boost"
+                self.cpp_info.includedirs = ["boost"]
+                self.cpp_info["Accumulators"].includedirs = ["boost/accumulators"]
+                self.cpp_info["Accumulators"].lib = "libaccumulators"
+                self.cpp_info["Containers"].includedirs = ["boost/containers"]
+                self.cpp_info["Containers"].lib = "libcontainers"
+                self.cpp_info["Containers"].deps = ["Accumulators"]
+                self.cpp_info["SuperContainers"].includedirs = ["boost/supercontainers"]
+                self.cpp_info["SuperContainers"].lib = "libsupercontainers"
+                self.cpp_info["SuperContainers"].deps = ["Containers"]
+        """)
+        consumer = textwrap.dedent("""
+        from conans import ConanFile
+
+        class Consumer(ConanFile):
+            requires = "dep/1.0@us/ch"
+
+            def build(self):
+                acc_includes = self.deps_cpp_info["dep"]["Accumulators"].includedirs
+                con_include = self.deps_cpp_info["dep"]["Containers"].includedirs
+                sup_include = self.deps_cpp_info["dep"]["SuperContainers"].includedirs
+                self.output.info("Name: %s" % self.deps_cpp_info["dep"].name)
+                self.output.info("Accumulators: %s" % acc_includes)
+                self.output.info("Containers: %s" % con_include)
+                self.output.info("SuperContainers: %s" % sup_include)
+                self.output.info("LIBS: %s" % self.deps_cpp_info["dep"].libs)
+        """)
+
+        client = TestClient()
+        client.save({"conanfile_dep.py": dep, "conanfile_consumer.py": consumer})
+        client.run("create conanfile_dep.py dep/1.0@us/ch")
+        client.run("create conanfile_consumer.py consumer/1.0@us/ch")
+        self.assertIn("Name: Boost", client.out)
+        self.assertIn("Accumulators: ['boost', 'boost/accumulators']", client.out)
+        self.assertIn("Containers: ['boost', 'boost/containers']", client.out)
+        self.assertIn("SuperContainers: ['boost', 'boost/supercontainers']", client.out)
+        self.assertIn("LIBS: ['libaccumulators', 'libcontainers', 'libsupercontainers']", client.out)
diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index 16398d21c48..3958bcfb2a3 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -207,20 +207,52 @@ def cpp_info_libs_components_fail_test(self):
                                                      "when Components are already in use"):
             info.libs = ["libgreet"]
 
-    def cppinfo_includedirs_test(self):
+    def cppinfo_dirs_test(self):
         folder = temp_folder()
         info = CppInfo(folder)
         info.name = "OpenSSL"
         info["OpenSSL"].includedirs = ["include"]
+        info["OpenSSL"].libdirs = ["lib"]
+        info["OpenSSL"].builddirs = ["build"]
+        info["OpenSSL"].bindirs = ["bin"]
+        info["OpenSSL"].resdirs = ["res"]
         info["Crypto"].includedirs = ["headers"]
+        info["Crypto"].libdirs = ["libraries"]
+        info["Crypto"].builddirs = ["build_scripts"]
+        info["Crypto"].bindirs = ["binaries"]
+        info["Crypto"].resdirs = ["resources"]
         self.assertEqual(["include"], info["OpenSSL"].includedirs)
+        self.assertEqual(["lib"], info["OpenSSL"].libdirs)
+        self.assertEqual(["", "build"], info["OpenSSL"].builddirs)
+        self.assertEqual(["bin"], info["OpenSSL"].bindirs)
+        self.assertEqual(["res"], info["OpenSSL"].resdirs)
         self.assertEqual(["include", "headers"], info["Crypto"].includedirs)
+        self.assertEqual(["lib", "libraries"], info["Crypto"].libdirs)
+        self.assertEqual(["", "build_scripts"], info["Crypto"].builddirs)
+        self.assertEqual(["bin", "binaries"], info["Crypto"].bindirs)
+        self.assertEqual(["res", "resources"], info["Crypto"].resdirs)
 
         info.includedirs = ["my_headers"]
+        info.libdirs = ["my_libraries"]
+        info.builddirs = ["my_build_scripts"]
+        info.bindirs = ["my_binaries"]
+        info.resdirs = ["my_resources"]
         self.assertEqual(["my_headers", "include"], info["OpenSSL"].includedirs)
+        self.assertEqual(["my_libraries", "lib"], info["OpenSSL"].libdirs)
+        self.assertEqual(["my_build_scripts", "build"], info["OpenSSL"].builddirs)
+        self.assertEqual(["my_binaries", "bin"], info["OpenSSL"].bindirs)
+        self.assertEqual(["my_resources", "res"], info["OpenSSL"].resdirs)
         self.assertEqual(["my_headers", "headers"], info["Crypto"].includedirs)
+        self.assertEqual(["my_libraries", "libraries"], info["Crypto"].libdirs)
+        self.assertEqual(["my_build_scripts", "build_scripts"], info["Crypto"].builddirs)
+        self.assertEqual(["my_binaries", "binaries"], info["Crypto"].bindirs)
+        self.assertEqual(["my_resources", "resources"], info["Crypto"].resdirs)
 
         info["Crypto"].includedirs = ["different_include"]
+        info["Crypto"].libdirs = ["different_lib"]
+        info["Crypto"].builddirs = ["different_build"]
+        info["Crypto"].bindirs = ["different_bin"]
+        info["Crypto"].resdirs = ["different_res"]
         self.assertEqual(["my_headers", "different_include"], info["Crypto"].includedirs)
 
         # FIXME:

From 14dc5040b73aada340f144ff68ba17a4efdc3f57 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Tue, 28 May 2019 18:10:48 +0200
Subject: [PATCH 08/47] Introduced DirList and completed tests

---
 conans/model/build_info.py                    | 47 +++++++++++++++----
 .../test/unittests/model/build_info_test.py   | 42 +++++++++++++++--
 2 files changed, 74 insertions(+), 15 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index 5772525f40a..7a7a26cac26 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -203,8 +203,7 @@ def exe(self, name):
 
     @property
     def includedirs(self):
-        includedirs = self._parent.includedirs + self._includedirs
-        return list(OrderedDict.fromkeys(includedirs))
+        return DirList(self._parent.includedirs, self._includedirs)
 
     @includedirs.setter
     def includedirs(self, value):
@@ -212,8 +211,7 @@ def includedirs(self, value):
 
     @property
     def libdirs(self):
-        libdirs = self._parent.libdirs + self._libdirs
-        return list(OrderedDict.fromkeys(libdirs))
+        return DirList(self._parent.libdirs, self._libdirs)
 
     @libdirs.setter
     def libdirs(self, value):
@@ -221,8 +219,7 @@ def libdirs(self, value):
 
     @property
     def resdirs(self):
-        resdirs = self._parent.resdirs + self._resdirs
-        return list(OrderedDict.fromkeys(resdirs))
+        return DirList(self._parent.resdirs, self._resdirs)
 
     @resdirs.setter
     def resdirs(self, value):
@@ -230,8 +227,7 @@ def resdirs(self, value):
 
     @property
     def bindirs(self):
-        bindirs = self._parent.bindirs + self._bindirs
-        return list(OrderedDict.fromkeys(bindirs))
+        return DirList(self._parent.bindirs, self._bindirs)
 
     @bindirs.setter
     def bindirs(self, value):
@@ -239,14 +235,45 @@ def bindirs(self, value):
 
     @property
     def builddirs(self):
-        builddirs = self._parent.builddirs + self._builddirs
-        return list(OrderedDict.fromkeys(builddirs))
+        return DirList(self._parent.builddirs, self._builddirs)
 
     @builddirs.setter
     def builddirs(self, value):
         self._builddirs = value
 
 
+class DirList(object):
+
+    def __init__(self, inherited_dirs=None, dirs=None):
+        self._inherited_dirs = inherited_dirs or []
+        self._dirs = dirs or []
+
+    @property
+    def _complete_list(self):
+        return list(OrderedDict.fromkeys(self._inherited_dirs + self._dirs))
+
+    def append(self, directory):
+        self._dirs.append(directory)
+
+    def extend(self, directories):
+        self._dirs.extend(directories)
+
+    def __getitem__(self, index):
+        return self._complete_list[index]
+
+    def __repr__(self):
+        return str(self._complete_list)
+
+    def __eq__(self, other):
+        return self._complete_list == other
+
+    def __len__(self):
+        return len(self._complete_list)
+
+    def insert(self, index, directory):
+        self._dirs.insert(index, directory)
+
+
 class _BaseDepsCppInfo(_CppInfo):
     def __init__(self):
         super(_BaseDepsCppInfo, self).__init__()
diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index 3958bcfb2a3..8902d9a0546 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -4,7 +4,7 @@
 
 from conans.client.generators import TXTGenerator
 from conans.errors import ConanException
-from conans.model.build_info import CppInfo, DepsCppInfo, Component
+from conans.model.build_info import CppInfo, DepsCppInfo, Component, DirList
 from conans.model.env_info import DepsEnvInfo, EnvInfo
 from conans.model.user_info import DepsUserInfo
 from conans.test.utils.test_files import temp_folder
@@ -254,8 +254,40 @@ def cppinfo_dirs_test(self):
         info["Crypto"].bindirs = ["different_bin"]
         info["Crypto"].resdirs = ["different_res"]
         self.assertEqual(["my_headers", "different_include"], info["Crypto"].includedirs)
+        self.assertEqual(["my_libraries", "different_lib"], info["Crypto"].libdirs)
+        self.assertEqual(["my_build_scripts", "different_build"], info["Crypto"].builddirs)
+        self.assertEqual(["my_binaries", "different_bin"], info["Crypto"].bindirs)
+        self.assertEqual(["my_resources", "different_res"], info["Crypto"].resdirs)
 
-        # FIXME:
-        # info["Crypto"].includedirs.extend(["another_include"])
-        # self.assertEqual(["my_headers", "different_include", "another_include"],
-        #                 info["Crypto"].includedirs)
+        info["Crypto"].includedirs.extend(["another_include"])
+        info["Crypto"].includedirs.append("another_other_include")
+        info["Crypto"].libdirs.extend(["another_lib"])
+        info["Crypto"].libdirs.append("another_other_lib")
+        info["Crypto"].builddirs.extend(["another_build"])
+        info["Crypto"].builddirs.append("another_other_build")
+        info["Crypto"].bindirs.extend(["another_bin"])
+        info["Crypto"].bindirs.append("another_other_bin")
+        info["Crypto"].resdirs.extend(["another_res"])
+        info["Crypto"].resdirs.append("another_other_res")
+        self.assertEqual(["my_headers", "different_include", "another_include",
+                          "another_other_include"], info["Crypto"].includedirs)
+        self.assertEqual(["my_libraries", "different_lib", "another_lib", "another_other_lib"],
+                         info["Crypto"].libdirs)
+        self.assertEqual(["my_build_scripts", "different_build", "another_build",
+                          "another_other_build"], info["Crypto"].builddirs)
+        self.assertEqual(["my_binaries", "different_bin", "another_bin", "another_other_bin"],
+                         info["Crypto"].bindirs)
+        self.assertEqual(["my_resources", "different_res", "another_res", "another_other_res"],
+                         info["Crypto"].resdirs)
+
+    def dirlist_test(self):
+        dirlist = DirList(["inc0"], ["inc1"])
+        dirlist.append("inc2")
+        self.assertEqual(["inc0", "inc1", "inc2"], dirlist)
+        dirlist.extend(["inc3", "inc4"])
+        self.assertEqual(["inc0", "inc1", "inc2", "inc3", "inc4"], dirlist)
+
+        dirlist.insert(0, "inc5")
+        self.assertEqual(["inc0", "inc5", "inc1", "inc2", "inc3", "inc4"], dirlist)
+        dirlist.insert(2, "inc6")
+        self.assertEqual(["inc0", "inc5", "inc1", "inc6", "inc2", "inc3", "inc4"], dirlist)

From b00a93c21cb987a954caeb31a87feb9df49ce624 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Wed, 29 May 2019 17:20:44 +0200
Subject: [PATCH 09/47] small changes for paths

---
 conans/model/build_info.py                    | 34 +++++++++++++++++--
 conans/test/integration/package_info_test.py  | 14 ++++++--
 .../test/unittests/model/build_info_test.py   |  8 ++---
 3 files changed, 48 insertions(+), 8 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index 7a7a26cac26..44eb6117a81 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -45,6 +45,30 @@ def __init__(self):
         self.description = None  # Description of the conan package
         # When package is editable, filter_empty=False, so empty dirs are maintained
         self.filter_empty = True
+        self._deps = OrderedDict()
+
+    @property
+    def libs(self):
+        if self._deps:
+            deps = [v for v in self._deps.values()]
+            deps_sorted = sorted(deps, key=lambda component: len(component.deps))
+            return [dep.lib for dep in deps_sorted if dep.lib is not None]
+        else:
+            return self._libs
+
+    @libs.setter
+    def libs(self, libs):
+        if self._deps:
+            raise ConanException("Setting first level libs is not supported when Components are "
+                                 "already in use")
+        self._libs = libs
+
+    def __getitem__(self, key):
+        if self._libs:
+            raise ConanException("Usage of Components with '.libs' values is not allowed")
+        if key not in self._deps.keys():
+            self._deps[key] = Component(self, key)
+        return self._deps[key]
 
     def _filter_paths(self, paths):
         abs_paths = [os.path.join(self.rootpath, p)
@@ -57,7 +81,12 @@ def _filter_paths(self, paths):
     @property
     def include_paths(self):
         if self._include_paths is None:
-            self._include_paths = self._filter_paths(self.includedirs)
+            includedirs = self.includedirs
+            for key, value in self._deps.items():
+                for directory in value.includedirs:
+                    if directory not in includedirs:
+                        includedirs.append(directory)
+            self._include_paths = self._filter_paths(includedirs)
         return self._include_paths
 
     @property
@@ -275,6 +304,7 @@ def insert(self, index, directory):
 
 
 class _BaseDepsCppInfo(_CppInfo):
+
     def __init__(self):
         super(_BaseDepsCppInfo, self).__init__()
 
@@ -289,7 +319,7 @@ def merge_lists(seq1, seq2):
         self.bindirs = merge_lists(self.bindirs, dep_cpp_info.bin_paths)
         self.resdirs = merge_lists(self.resdirs, dep_cpp_info.res_paths)
         self.builddirs = merge_lists(self.builddirs, dep_cpp_info.build_paths)
-        self.libs = merge_lists(self.libs, dep_cpp_info.libs)
+        self.libs = merge_lists(self.libs, dep_cpp_info._libs)
         self.rootpaths.append(dep_cpp_info.rootpath)
 
         # Note these are in reverse order
diff --git a/conans/test/integration/package_info_test.py b/conans/test/integration/package_info_test.py
index 22f6d0c3f7d..c875be81762 100644
--- a/conans/test/integration/package_info_test.py
+++ b/conans/test/integration/package_info_test.py
@@ -20,7 +20,7 @@ class HelloConan(ConanFile):
     options = {"switch": ["1",  "0"]}
     default_options = "switch=0"
     %s
-    
+
     def build(self):
         self.output.warn("Env var MYVAR={0}.".format(os.getenv("MYVAR", "")))
 
@@ -53,6 +53,10 @@ def package_info_components_test(self):
         from conans import ConanFile
 
         class Dep(ConanFile):
+            exports_sources = "*"
+
+            def package(self):
+                self.copy("*")
 
             def package_info(self):
                 self.cpp_info.name = "Boost"
@@ -81,10 +85,16 @@ def build(self):
                 self.output.info("Containers: %s" % con_include)
                 self.output.info("SuperContainers: %s" % sup_include)
                 self.output.info("LIBS: %s" % self.deps_cpp_info["dep"].libs)
+                print("INCLUDE_PATHS: %s" % self.deps_cpp_info["dep"].include_paths)
+                print("INCLUDE_PATHS: %s" % self.deps_cpp_info.include_paths)
         """)
 
         client = TestClient()
-        client.save({"conanfile_dep.py": dep, "conanfile_consumer.py": consumer})
+        client.save({"conanfile_dep.py": dep, "conanfile_consumer.py": consumer,
+                     "boost/boost.h": "",
+                     "boost/accumulators/accumulators.h": "",
+                     "boost/containers/containers.h": "",
+                     "boost/supercontainers/supercontainers.h": ""})
         client.run("create conanfile_dep.py dep/1.0@us/ch")
         client.run("create conanfile_consumer.py consumer/1.0@us/ch")
         self.assertIn("Name: Boost", client.out)
diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index 8902d9a0546..3cc86385b32 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -180,13 +180,13 @@ def basic_components_test(self):
         component = cpp_info["my_component"]
         self.assertIn(component.name, "my_component")
         component.lib = "libhola"
-        self.assertEquals(component.lib, "libhola")
-        with self.assertRaisesRegexp(ConanException, "'.lib' is already set for this Component"):
+        self.assertEqual(component.lib, "libhola")
+        with self.assertRaisesRegex(ConanException, "'.lib' is already set for this Component"):
             component.exe = "hola.exe"
         component.lib = None
         component.exe = "hola.exe"
-        self.assertEquals(component.lib, None)
-        with self.assertRaisesRegexp(ConanException, "'.exe' is already set for this Component"):
+        self.assertEqual(component.lib, None)
+        with self.assertRaisesRegex(ConanException, "'.exe' is already set for this Component"):
             component.lib = "libhola"
 
     def cpp_info_libs_components_fail_test(self):

From 9b53334842c5852b562192604629a2e8e84bff08 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Thu, 13 Jun 2019 18:37:48 +0200
Subject: [PATCH 10/47] Removed DirList and added system_deps

---
 conans/model/build_info.py                    | 201 +++++++++---------
 conans/test/integration/package_info_test.py  |  28 ++-
 .../test/unittests/model/build_info_test.py   |  88 ++++----
 3 files changed, 162 insertions(+), 155 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index 44eb6117a81..ffe26601cc7 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -10,6 +10,7 @@
 DEFAULT_BIN = "bin"
 DEFAULT_RES = "res"
 DEFAULT_SHARE = "share"
+DEFAULT_BUILD = ""
 
 
 class _CppInfo(object):
@@ -28,6 +29,7 @@ def __init__(self):
         self.builddirs = []
         self.rootpaths = []
         self._libs = []  # The libs to link against
+        self._exes = []
         self.defines = []  # preprocessor definitions
         self.cflags = []  # pure C flags
         self.cxxflags = []  # C++ compilation flags
@@ -63,6 +65,21 @@ def libs(self, libs):
                                  "already in use")
         self._libs = libs
 
+    @property
+    def exes(self):
+        if self._deps:
+            deps = [v for v in self._deps.values()]
+            return [dep.exe for dep in deps if dep.exe is not None]
+        else:
+            return self._exes
+
+    @exes.setter
+    def libs(self, libs):
+        if self._deps:
+            raise ConanException("Setting first level exes is not supported when Components are "
+                                 "already in use")
+        self._exes = exes
+
     def __getitem__(self, key):
         if self._libs:
             raise ConanException("Usage of Components with '.libs' values is not allowed")
@@ -78,46 +95,41 @@ def _filter_paths(self, paths):
         else:
             return abs_paths
 
+    def _get_paths(self, path_name):
+        if getattr(self, "_%s_paths" % path_name) is None:
+            if self._deps:
+                self.__dict__["_%s_paths" % path_name] = []
+                for dep_value in self._deps.values():
+                    self.__dict__["_%s_paths" % path_name].extend(
+                            self._filter_paths(getattr(dep_value, "%s_paths" % path_name)))
+            else:
+                self.__dict__["_%s_paths" % path_name] = self._filter_paths(
+                        getattr(self, "%sdirs" % path_name))
+        return getattr(self, "_%s_paths" % path_name)
+
     @property
     def include_paths(self):
-        if self._include_paths is None:
-            includedirs = self.includedirs
-            for key, value in self._deps.items():
-                for directory in value.includedirs:
-                    if directory not in includedirs:
-                        includedirs.append(directory)
-            self._include_paths = self._filter_paths(includedirs)
-        return self._include_paths
+        return self._get_paths("include")
 
     @property
     def lib_paths(self):
-        if self._lib_paths is None:
-            self._lib_paths = self._filter_paths(self.libdirs)
-        return self._lib_paths
+        return self._get_paths("lib")
 
     @property
     def src_paths(self):
-        if self._src_paths is None:
-            self._src_paths = self._filter_paths(self.srcdirs)
-        return self._src_paths
+        return self._get_paths("src")
 
     @property
     def bin_paths(self):
-        if self._bin_paths is None:
-            self._bin_paths = self._filter_paths(self.bindirs)
-        return self._bin_paths
+        return self._get_paths("bin")
 
     @property
     def build_paths(self):
-        if self._build_paths is None:
-            self._build_paths = self._filter_paths(self.builddirs)
-        return self._build_paths
+        return self._get_paths("build")
 
     @property
     def res_paths(self):
-        if self._res_paths is None:
-            self._res_paths = self._filter_paths(self.resdirs)
-        return self._res_paths
+        return self._get_paths("res")
 
     # Compatibility for 'cppflags' (old style property to allow decoration)
     @deprecation.deprecated(deprecated_in="1.13", removed_in="2.0", details="Use 'cxxflags' instead")
@@ -144,18 +156,52 @@ def __init__(self, root_folder):
         self.libdirs.append(DEFAULT_LIB)
         self.bindirs.append(DEFAULT_BIN)
         self.resdirs.append(DEFAULT_RES)
-        self.builddirs.append("")
+        self.builddirs.append(DEFAULT_BUILD)
         # public_deps is needed to accumulate list of deps for cmake targets
         self.public_deps = []
         self.configs = {}
         self._deps = OrderedDict()
 
+    def _check_dirs_values(self):
+        default_dirs_mapping = {
+            "includedirs": [DEFAULT_INCLUDE],
+            "libdirs": [DEFAULT_LIB],
+            "bindirs": [DEFAULT_BIN],
+            "resdirs": [DEFAULT_RES],
+            "builddirs": [DEFAULT_BUILD],
+            "srcdirs": []
+        }
+        msg_template = "Using Components and global '{}' values ('{}') is not supported"
+        for dir_name in ["includedirs", "libdirs", "bindirs", "builddirs"]:
+            dirs_value = getattr(self, dir_name)
+            if dirs_value is not None and dirs_value != default_dirs_mapping[dir_name]:
+                raise ConanException(msg_template.format(dir_name, dirs_value))
+
+    def _clear_dirs_values(self):
+        default_dirs_mapping = {
+            "includedirs": [DEFAULT_INCLUDE],
+            "libdirs": [DEFAULT_LIB],
+            "bindirs": [DEFAULT_BIN],
+            "resdirs": [DEFAULT_RES],
+            "builddirs": [DEFAULT_BUILD],
+            "srcdirs": []
+        }
+        for dir_name in ["includedirs", "libdirs", "bindirs", "builddirs"]:
+            if getattr(self, dir_name) == default_dirs_mapping[dir_name]:
+                self.__dict__[dir_name] = None
+
     @property
     def libs(self):
         if self._deps:
             deps = [v for v in self._deps.values()]
             deps_sorted = sorted(deps, key=lambda component: len(component.deps))
-            return [dep.lib for dep in deps_sorted if dep.lib is not None]
+            result = []
+            for dep in deps_sorted:
+                for sys_dep in dep.system_deps:
+                    if sys_dep not in result:
+                        result.append(sys_dep)
+                result.append(dep.lib)
+            return result
         else:
             return self._libs
 
@@ -167,10 +213,12 @@ def libs(self, libs):
         self._libs = libs
 
     def __getitem__(self, key):
-        if self._libs:
-            raise ConanException("Usage of Components with '.libs' values is not allowed")
+        if self._libs or self._exes:
+            raise ConanException("Usage of Components with '.libs' or '.exes' values is not allowed")
+        self._clear_dirs_values()
+        self._check_dirs_values()
         if key not in self._deps.keys():
-            self._deps[key] = Component(self, key)
+            self._deps[key] = Component(key, self.rootpath)
         return self._deps[key]
 
     def __getattr__(self, config):
@@ -191,24 +239,34 @@ def _get_cpp_info():
 
 class Component(object):
 
-    def __init__(self, parent, name):
-        self._parent = parent
+    def __init__(self, name, root_folder):
+        self._rootpath = root_folder
         self.name = name
         self.deps = []
         self._lib = None
         self._exe = None
         self.system_deps = []
-        self._includedirs = []
-        self._libdirs = []
-        self._resdirs = []
-        self._bindirs = []
-        self._builddirs = []
+        self.includedirs = []
+        self.libdirs = []
+        self.resdirs = []
+        self.bindirs = []
+        self.builddirs = []
+        self.srcdirs = []
         self.defines = []
         self.cflags = []
         self.cppflags = []
         self.cxxflags = []
         self.sharedlinkflags = []
         self.exelinkflags = []
+        self._filter_empty = True
+
+    def _filter_paths(self, paths):
+        abs_paths = [os.path.join(self._rootpath, p)
+                     if not os.path.isabs(p) else p for p in paths]
+        if self._filter_empty:
+            return [p for p in abs_paths if os.path.isdir(p)]
+        else:
+            return abs_paths
 
     @property
     def lib(self):
@@ -231,76 +289,28 @@ def exe(self, name):
         self._exe = name
 
     @property
-    def includedirs(self):
-        return DirList(self._parent.includedirs, self._includedirs)
-
-    @includedirs.setter
-    def includedirs(self, value):
-        self._includedirs = value
+    def include_paths(self):
+        return self._filter_paths(self.includedirs)
 
     @property
-    def libdirs(self):
-        return DirList(self._parent.libdirs, self._libdirs)
-
-    @libdirs.setter
-    def libdirs(self, value):
-        self._libdirs = value
+    def lib_paths(self):
+        return self._filter_paths(self.libdirs)
 
     @property
-    def resdirs(self):
-        return DirList(self._parent.resdirs, self._resdirs)
-
-    @resdirs.setter
-    def resdirs(self, value):
-        self._resdirs = value
+    def bin_paths(self):
+        return self._filter_paths(self.bindirs)
 
     @property
-    def bindirs(self):
-        return DirList(self._parent.bindirs, self._bindirs)
-
-    @bindirs.setter
-    def bindirs(self, value):
-        self._bindirs = value
+    def build_paths(self):
+        return self._filter_paths(self.builddirs)
 
     @property
-    def builddirs(self):
-        return DirList(self._parent.builddirs, self._builddirs)
-
-    @builddirs.setter
-    def builddirs(self, value):
-        self._builddirs = value
-
-
-class DirList(object):
-
-    def __init__(self, inherited_dirs=None, dirs=None):
-        self._inherited_dirs = inherited_dirs or []
-        self._dirs = dirs or []
+    def res_paths(self):
+        return self._filter_paths(self.resdirs)
 
     @property
-    def _complete_list(self):
-        return list(OrderedDict.fromkeys(self._inherited_dirs + self._dirs))
-
-    def append(self, directory):
-        self._dirs.append(directory)
-
-    def extend(self, directories):
-        self._dirs.extend(directories)
-
-    def __getitem__(self, index):
-        return self._complete_list[index]
-
-    def __repr__(self):
-        return str(self._complete_list)
-
-    def __eq__(self, other):
-        return self._complete_list == other
-
-    def __len__(self):
-        return len(self._complete_list)
-
-    def insert(self, index, directory):
-        self._dirs.insert(index, directory)
+    def src_paths(self):
+        return self._filter_paths(self.srcdirs)
 
 
 class _BaseDepsCppInfo(_CppInfo):
@@ -402,3 +412,4 @@ def update_deps_cpp_info(self, dep_cpp_info):
         assert isinstance(dep_cpp_info, DepsCppInfo)
         for pkg_name, cpp_info in dep_cpp_info.dependencies:
             self.update(cpp_info, pkg_name)
+
diff --git a/conans/test/integration/package_info_test.py b/conans/test/integration/package_info_test.py
index c875be81762..f3872b7e1a8 100644
--- a/conans/test/integration/package_info_test.py
+++ b/conans/test/integration/package_info_test.py
@@ -1,8 +1,10 @@
+import os
 import textwrap
 import unittest
 
+from conans.model.ref import ConanFileReference, PackageReference
 from conans.paths import CONANFILE, CONANFILE_TXT
-from conans.test.utils.tools import TestClient
+from conans.test.utils.tools import TestClient, NO_SETTINGS_PACKAGE_ID
 
 
 class TestPackageInfo(unittest.TestCase):
@@ -50,6 +52,7 @@ def package_info(self):
 
     def package_info_components_test(self):
         dep = textwrap.dedent("""
+        import os
         from conans import ConanFile
 
         class Dep(ConanFile):
@@ -60,8 +63,7 @@ def package(self):
 
             def package_info(self):
                 self.cpp_info.name = "Boost"
-                self.cpp_info.includedirs = ["boost"]
-                self.cpp_info["Accumulators"].includedirs = ["boost/accumulators"]
+                self.cpp_info["Accumulators"].includedirs = [os.path.join("boost", "accumulators")]
                 self.cpp_info["Accumulators"].lib = "libaccumulators"
                 self.cpp_info["Containers"].includedirs = ["boost/containers"]
                 self.cpp_info["Containers"].lib = "libcontainers"
@@ -77,9 +79,9 @@ class Consumer(ConanFile):
             requires = "dep/1.0@us/ch"
 
             def build(self):
-                acc_includes = self.deps_cpp_info["dep"]["Accumulators"].includedirs
-                con_include = self.deps_cpp_info["dep"]["Containers"].includedirs
-                sup_include = self.deps_cpp_info["dep"]["SuperContainers"].includedirs
+                acc_includes = self.deps_cpp_info["dep"]["Accumulators"].include_paths
+                con_include = self.deps_cpp_info["dep"]["Containers"].include_paths
+                sup_include = self.deps_cpp_info["dep"]["SuperContainers"].include_paths
                 self.output.info("Name: %s" % self.deps_cpp_info["dep"].name)
                 self.output.info("Accumulators: %s" % acc_includes)
                 self.output.info("Containers: %s" % con_include)
@@ -95,10 +97,18 @@ def build(self):
                      "boost/accumulators/accumulators.h": "",
                      "boost/containers/containers.h": "",
                      "boost/supercontainers/supercontainers.h": ""})
+        dep_ref = ConanFileReference("dep", "1.0", "us", "ch")
+        dep_pref = PackageReference(dep_ref, NO_SETTINGS_PACKAGE_ID)
+        print("package_folder1: ", client.cache.package_layout(dep_ref).package(dep_pref))
         client.run("create conanfile_dep.py dep/1.0@us/ch")
         client.run("create conanfile_consumer.py consumer/1.0@us/ch")
+        package_folder = client.cache.package_layout(dep_ref).package(dep_pref)
+        accumulators_expected = os.path.join(package_folder, "boost", "accumulators")
+        print("package_folder2: ", client.cache.package_layout(dep_ref).package(dep_pref))
         self.assertIn("Name: Boost", client.out)
-        self.assertIn("Accumulators: ['boost', 'boost/accumulators']", client.out)
-        self.assertIn("Containers: ['boost', 'boost/containers']", client.out)
-        self.assertIn("SuperContainers: ['boost', 'boost/supercontainers']", client.out)
+        print("EXPECTED: ", "Accumulators: ['%s']" % accumulators_expected)
+        print("RESULT: ", client.out)
         self.assertIn("LIBS: ['libaccumulators', 'libcontainers', 'libsupercontainers']", client.out)
+        #FIXME: self.assertIn("Accumulators: ['%s']" % accumulators_expected, client.out)
+        #FIXME: self.assertIn("Containers: ['boost', 'boost/containers']", client.out)
+        #FIXME: self.assertIn("SuperContainers: ['boost', 'boost/supercontainers']", client.out)
diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index 3cc86385b32..c42e784a6d9 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -4,7 +4,7 @@
 
 from conans.client.generators import TXTGenerator
 from conans.errors import ConanException
-from conans.model.build_info import CppInfo, DepsCppInfo, Component, DirList
+from conans.model.build_info import CppInfo, DepsCppInfo, Component
 from conans.model.env_info import DepsEnvInfo, EnvInfo
 from conans.model.user_info import DepsUserInfo
 from conans.test.utils.test_files import temp_folder
@@ -170,6 +170,7 @@ def cpp_info_test(self):
         info.libdirs.append(abs_lib)
         info.bindirs.append(abs_bin)
         info.bindirs.append("local_bindir")
+        print("result: ", info.include_paths, info.lib_paths, info.bin_paths)
         self.assertEqual(info.include_paths, [os.path.join(folder, "include"), abs_include])
         self.assertEqual(info.lib_paths, [os.path.join(folder, "lib"), abs_lib])
         self.assertEqual(info.bin_paths, [abs_bin,
@@ -181,12 +182,12 @@ def basic_components_test(self):
         self.assertIn(component.name, "my_component")
         component.lib = "libhola"
         self.assertEqual(component.lib, "libhola")
-        with self.assertRaisesRegex(ConanException, "'.lib' is already set for this Component"):
+        with self.assertRaisesRegexp(ConanException, "'.lib' is already set for this Component"):
             component.exe = "hola.exe"
         component.lib = None
         component.exe = "hola.exe"
         self.assertEqual(component.lib, None)
-        with self.assertRaisesRegex(ConanException, "'.exe' is already set for this Component"):
+        with self.assertRaisesRegexp(ConanException, "'.exe' is already set for this Component"):
             component.lib = "libhola"
 
     def cpp_info_libs_components_fail_test(self):
@@ -197,8 +198,8 @@ def cpp_info_libs_components_fail_test(self):
         info.name = "Greetings"
         self.assertIn(info.name, "Greetings")
         info.libs = ["libgreet"]
-        with self.assertRaisesRegexp(ConanException, "Usage of Components with '.libs' values is "
-                                                     "not allowed"):
+        with self.assertRaisesRegexp(ConanException, "Usage of Components with '.libs' or '.exes' "
+                                                     "values is not allowed"):
             info["hola"].exe = "hola.exe"
 
         info.libs = []
@@ -207,6 +208,19 @@ def cpp_info_libs_components_fail_test(self):
                                                      "when Components are already in use"):
             info.libs = ["libgreet"]
 
+    def cpp_info_libs_system_deps_order_test(self):
+        info = CppInfo(None)
+        info["LIB1"].lib = "lib1"
+        info["LIB1"].system_deps = ["sys1", "sys11"]
+        info["LIB1"].deps = ["LIB2"]
+        info["LIB2"].lib = "lib2"
+        info["LIB2"].system_deps = ["sys2"]
+        info["LIB1"].deps = ["LIB3"]
+        info["LIB3"].lib = "lib3"
+        info["LIB3"].system_deps = ["sys3", "sys2"]
+        self.assertEqual(['sys2', 'lib2', 'sys3', 'lib3', 'sys1', 'sys11', 'lib1'], info.libs)
+        print(info.libs)
+
     def cppinfo_dirs_test(self):
         folder = temp_folder()
         info = CppInfo(folder)
@@ -223,41 +237,25 @@ def cppinfo_dirs_test(self):
         info["Crypto"].resdirs = ["resources"]
         self.assertEqual(["include"], info["OpenSSL"].includedirs)
         self.assertEqual(["lib"], info["OpenSSL"].libdirs)
-        self.assertEqual(["", "build"], info["OpenSSL"].builddirs)
+        self.assertEqual(["build"], info["OpenSSL"].builddirs)
         self.assertEqual(["bin"], info["OpenSSL"].bindirs)
         self.assertEqual(["res"], info["OpenSSL"].resdirs)
-        self.assertEqual(["include", "headers"], info["Crypto"].includedirs)
-        self.assertEqual(["lib", "libraries"], info["Crypto"].libdirs)
-        self.assertEqual(["", "build_scripts"], info["Crypto"].builddirs)
-        self.assertEqual(["bin", "binaries"], info["Crypto"].bindirs)
-        self.assertEqual(["res", "resources"], info["Crypto"].resdirs)
-
-        info.includedirs = ["my_headers"]
-        info.libdirs = ["my_libraries"]
-        info.builddirs = ["my_build_scripts"]
-        info.bindirs = ["my_binaries"]
-        info.resdirs = ["my_resources"]
-        self.assertEqual(["my_headers", "include"], info["OpenSSL"].includedirs)
-        self.assertEqual(["my_libraries", "lib"], info["OpenSSL"].libdirs)
-        self.assertEqual(["my_build_scripts", "build"], info["OpenSSL"].builddirs)
-        self.assertEqual(["my_binaries", "bin"], info["OpenSSL"].bindirs)
-        self.assertEqual(["my_resources", "res"], info["OpenSSL"].resdirs)
-        self.assertEqual(["my_headers", "headers"], info["Crypto"].includedirs)
-        self.assertEqual(["my_libraries", "libraries"], info["Crypto"].libdirs)
-        self.assertEqual(["my_build_scripts", "build_scripts"], info["Crypto"].builddirs)
-        self.assertEqual(["my_binaries", "binaries"], info["Crypto"].bindirs)
-        self.assertEqual(["my_resources", "resources"], info["Crypto"].resdirs)
+        self.assertEqual(["headers"], info["Crypto"].includedirs)
+        self.assertEqual(["libraries"], info["Crypto"].libdirs)
+        self.assertEqual(["build_scripts"], info["Crypto"].builddirs)
+        self.assertEqual(["binaries"], info["Crypto"].bindirs)
+        self.assertEqual(["resources"], info["Crypto"].resdirs)
 
         info["Crypto"].includedirs = ["different_include"]
         info["Crypto"].libdirs = ["different_lib"]
         info["Crypto"].builddirs = ["different_build"]
         info["Crypto"].bindirs = ["different_bin"]
         info["Crypto"].resdirs = ["different_res"]
-        self.assertEqual(["my_headers", "different_include"], info["Crypto"].includedirs)
-        self.assertEqual(["my_libraries", "different_lib"], info["Crypto"].libdirs)
-        self.assertEqual(["my_build_scripts", "different_build"], info["Crypto"].builddirs)
-        self.assertEqual(["my_binaries", "different_bin"], info["Crypto"].bindirs)
-        self.assertEqual(["my_resources", "different_res"], info["Crypto"].resdirs)
+        self.assertEqual(["different_include"], info["Crypto"].includedirs)
+        self.assertEqual(["different_lib"], info["Crypto"].libdirs)
+        self.assertEqual(["different_build"], info["Crypto"].builddirs)
+        self.assertEqual(["different_bin"], info["Crypto"].bindirs)
+        self.assertEqual(["different_res"], info["Crypto"].resdirs)
 
         info["Crypto"].includedirs.extend(["another_include"])
         info["Crypto"].includedirs.append("another_other_include")
@@ -269,25 +267,13 @@ def cppinfo_dirs_test(self):
         info["Crypto"].bindirs.append("another_other_bin")
         info["Crypto"].resdirs.extend(["another_res"])
         info["Crypto"].resdirs.append("another_other_res")
-        self.assertEqual(["my_headers", "different_include", "another_include",
-                          "another_other_include"], info["Crypto"].includedirs)
-        self.assertEqual(["my_libraries", "different_lib", "another_lib", "another_other_lib"],
+        self.assertEqual(["different_include", "another_include", "another_other_include"],
+                         info["Crypto"].includedirs)
+        self.assertEqual(["different_lib", "another_lib", "another_other_lib"],
                          info["Crypto"].libdirs)
-        self.assertEqual(["my_build_scripts", "different_build", "another_build",
-                          "another_other_build"], info["Crypto"].builddirs)
-        self.assertEqual(["my_binaries", "different_bin", "another_bin", "another_other_bin"],
+        self.assertEqual(["different_build", "another_build", "another_other_build"],
+                         info["Crypto"].builddirs)
+        self.assertEqual(["different_bin", "another_bin", "another_other_bin"],
                          info["Crypto"].bindirs)
-        self.assertEqual(["my_resources", "different_res", "another_res", "another_other_res"],
+        self.assertEqual(["different_res", "another_res", "another_other_res"],
                          info["Crypto"].resdirs)
-
-    def dirlist_test(self):
-        dirlist = DirList(["inc0"], ["inc1"])
-        dirlist.append("inc2")
-        self.assertEqual(["inc0", "inc1", "inc2"], dirlist)
-        dirlist.extend(["inc3", "inc4"])
-        self.assertEqual(["inc0", "inc1", "inc2", "inc3", "inc4"], dirlist)
-
-        dirlist.insert(0, "inc5")
-        self.assertEqual(["inc0", "inc5", "inc1", "inc2", "inc3", "inc4"], dirlist)
-        dirlist.insert(2, "inc6")
-        self.assertEqual(["inc0", "inc5", "inc1", "inc6", "inc2", "inc3", "inc4"], dirlist)

From 61981a20d3335937217abada94bcb01b04dad2fe Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Fri, 14 Jun 2019 11:51:24 +0200
Subject: [PATCH 11/47] fix exes

---
 conans/model/build_info.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index ffe26601cc7..5e281ba6753 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -74,7 +74,7 @@ def exes(self):
             return self._exes
 
     @exes.setter
-    def libs(self, libs):
+    def exes(self, exes):
         if self._deps:
             raise ConanException("Setting first level exes is not supported when Components are "
                                  "already in use")

From 55096381f28cf632bfc5f67654ab6920f17944fd Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Mon, 17 Jun 2019 11:44:02 +0200
Subject: [PATCH 12/47] fixed deprecation warning in tests

---
 conans/test/unittests/model/build_info_test.py | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index c42e784a6d9..e0db89642db 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -1,4 +1,5 @@
 import os
+import six
 import unittest
 from collections import defaultdict, namedtuple
 
@@ -182,12 +183,12 @@ def basic_components_test(self):
         self.assertIn(component.name, "my_component")
         component.lib = "libhola"
         self.assertEqual(component.lib, "libhola")
-        with self.assertRaisesRegexp(ConanException, "'.lib' is already set for this Component"):
+        with six.assertRaisesRegex(self, ConanException, "'.lib' is already set for this Component"):
             component.exe = "hola.exe"
         component.lib = None
         component.exe = "hola.exe"
         self.assertEqual(component.lib, None)
-        with self.assertRaisesRegexp(ConanException, "'.exe' is already set for this Component"):
+        with six.assertRaisesRegex(self, ConanException, "'.exe' is already set for this Component"):
             component.lib = "libhola"
 
     def cpp_info_libs_components_fail_test(self):
@@ -198,14 +199,14 @@ def cpp_info_libs_components_fail_test(self):
         info.name = "Greetings"
         self.assertIn(info.name, "Greetings")
         info.libs = ["libgreet"]
-        with self.assertRaisesRegexp(ConanException, "Usage of Components with '.libs' or '.exes' "
-                                                     "values is not allowed"):
+        with six.assertRaisesRegex(self, ConanException, "Usage of Components with '.libs' or "
+                                                         "'.exes' values is not allowed"):
             info["hola"].exe = "hola.exe"
 
         info.libs = []
         info["greet"].exe = "libgreet"
-        with self.assertRaisesRegexp(ConanException, "Setting first level libs is not supported "
-                                                     "when Components are already in use"):
+        with six.assertRaisesRegex(self, ConanException, "Setting first level libs is not supported "
+                                                         "when Components are already in use"):
             info.libs = ["libgreet"]
 
     def cpp_info_libs_system_deps_order_test(self):

From f6df3651de8688a16329296ff5c465e5962c26b6 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Mon, 17 Jun 2019 11:46:53 +0200
Subject: [PATCH 13/47] Fixed cpp_info libs value in json output

---
 conans/client/recorder/action_recorder.py | 5 +++--
 conans/model/build_info.py                | 5 ++---
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/conans/client/recorder/action_recorder.py b/conans/client/recorder/action_recorder.py
index b5e81d89534..9efa287a76a 100644
--- a/conans/client/recorder/action_recorder.py
+++ b/conans/client/recorder/action_recorder.py
@@ -25,7 +25,7 @@
 def _cpp_info_to_dict(cpp_info):
     doc = {}
     for it, value in vars(cpp_info).items():
-        if it.startswith("_") or not value:
+        if (it.startswith("_") and it != "_libs") or not value:
             continue
 
         if it == "configs":
@@ -35,7 +35,8 @@ def _cpp_info_to_dict(cpp_info):
             doc["configs"] = configs_data
             continue
 
-        doc[it] = value
+        key = "libs" if it == "_libs" else it
+        doc[key] = value
     return doc
 
 
diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index 5e281ba6753..6d0a70e95af 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -54,9 +54,8 @@ def libs(self):
         if self._deps:
             deps = [v for v in self._deps.values()]
             deps_sorted = sorted(deps, key=lambda component: len(component.deps))
-            return [dep.lib for dep in deps_sorted if dep.lib is not None]
-        else:
-            return self._libs
+            self._libs = [dep.lib for dep in deps_sorted if dep.lib is not None]
+        return self._libs
 
     @libs.setter
     def libs(self, libs):

From e88b6f10da2ccec9085f22d2d523d4940ec3a809 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Mon, 17 Jun 2019 12:35:32 +0200
Subject: [PATCH 14/47] fix test

---
 conans/test/integration/package_info_test.py | 23 +++++++++-----------
 1 file changed, 10 insertions(+), 13 deletions(-)

diff --git a/conans/test/integration/package_info_test.py b/conans/test/integration/package_info_test.py
index f3872b7e1a8..61bd9bea3dc 100644
--- a/conans/test/integration/package_info_test.py
+++ b/conans/test/integration/package_info_test.py
@@ -31,8 +31,8 @@ def package_info(self):
             self.env_info.MYVAR = "foo"
         else:
             self.env_info.MYVAR = "bar"
-
 '''
+
         for index in range(4):
             requires = "requires = 'Lib%s/1.0@conan/stable'" % index if index > 0 else ""
             conanfile = conanfile_tmp % ("Lib%s" % (index + 1), requires)
@@ -65,10 +65,11 @@ def package_info(self):
                 self.cpp_info.name = "Boost"
                 self.cpp_info["Accumulators"].includedirs = [os.path.join("boost", "accumulators")]
                 self.cpp_info["Accumulators"].lib = "libaccumulators"
-                self.cpp_info["Containers"].includedirs = ["boost/containers"]
+                self.cpp_info["Containers"].includedirs = [os.path.join("boost", "containers")]
                 self.cpp_info["Containers"].lib = "libcontainers"
                 self.cpp_info["Containers"].deps = ["Accumulators"]
-                self.cpp_info["SuperContainers"].includedirs = ["boost/supercontainers"]
+                self.cpp_info["SuperContainers"].includedirs = [os.path.join("boost",
+                                                                             "supercontainers")]
                 self.cpp_info["SuperContainers"].lib = "libsupercontainers"
                 self.cpp_info["SuperContainers"].deps = ["Containers"]
         """)
@@ -87,8 +88,6 @@ def build(self):
                 self.output.info("Containers: %s" % con_include)
                 self.output.info("SuperContainers: %s" % sup_include)
                 self.output.info("LIBS: %s" % self.deps_cpp_info["dep"].libs)
-                print("INCLUDE_PATHS: %s" % self.deps_cpp_info["dep"].include_paths)
-                print("INCLUDE_PATHS: %s" % self.deps_cpp_info.include_paths)
         """)
 
         client = TestClient()
@@ -99,16 +98,14 @@ def build(self):
                      "boost/supercontainers/supercontainers.h": ""})
         dep_ref = ConanFileReference("dep", "1.0", "us", "ch")
         dep_pref = PackageReference(dep_ref, NO_SETTINGS_PACKAGE_ID)
-        print("package_folder1: ", client.cache.package_layout(dep_ref).package(dep_pref))
         client.run("create conanfile_dep.py dep/1.0@us/ch")
         client.run("create conanfile_consumer.py consumer/1.0@us/ch")
         package_folder = client.cache.package_layout(dep_ref).package(dep_pref)
-        accumulators_expected = os.path.join(package_folder, "boost", "accumulators")
-        print("package_folder2: ", client.cache.package_layout(dep_ref).package(dep_pref))
+        accumulators_expected = [os.path.join(package_folder, "boost", "accumulators")]
+        containers_expected = [os.path.join(package_folder, "boost", "containers")]
+        supercontainers_expected = [os.path.join(package_folder, "boost", "supercontainers")]
         self.assertIn("Name: Boost", client.out)
-        print("EXPECTED: ", "Accumulators: ['%s']" % accumulators_expected)
-        print("RESULT: ", client.out)
         self.assertIn("LIBS: ['libaccumulators', 'libcontainers', 'libsupercontainers']", client.out)
-        #FIXME: self.assertIn("Accumulators: ['%s']" % accumulators_expected, client.out)
-        #FIXME: self.assertIn("Containers: ['boost', 'boost/containers']", client.out)
-        #FIXME: self.assertIn("SuperContainers: ['boost', 'boost/supercontainers']", client.out)
+        self.assertIn("Accumulators: %s" % accumulators_expected, client.out)
+        self.assertIn("Containers: %s" % containers_expected, client.out)
+        self.assertIn("SuperContainers: %s" % supercontainers_expected, client.out)

From 6c8bd403a8c21da7de734f9f0982fa333a4f1724 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Wed, 19 Jun 2019 11:08:12 +0200
Subject: [PATCH 15/47] Add description to _get_paths()

---
 conans/model/build_info.py | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index 6d0a70e95af..e08654a16cb 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -95,6 +95,13 @@ def _filter_paths(self, paths):
             return abs_paths
 
     def _get_paths(self, path_name):
+        """
+        Get the absolute paths either composing the lists from components or from the global
+        variables. Also filter the values checking if the folders exist or not. This paths are
+        calculated once and then the result is cached.
+        :param path_name: name of the path variable to get (include_paths, res_paths...)
+        :return: List of absolute paths
+        """
         if getattr(self, "_%s_paths" % path_name) is None:
             if self._deps:
                 self.__dict__["_%s_paths" % path_name] = []

From 831172c97f5d279debd1344132db0ae05189985d Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Wed, 19 Jun 2019 11:08:55 +0200
Subject: [PATCH 16/47] removed prints

---
 conans/test/unittests/model/build_info_test.py | 2 --
 1 file changed, 2 deletions(-)

diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index e0db89642db..cec1e3b6478 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -171,7 +171,6 @@ def cpp_info_test(self):
         info.libdirs.append(abs_lib)
         info.bindirs.append(abs_bin)
         info.bindirs.append("local_bindir")
-        print("result: ", info.include_paths, info.lib_paths, info.bin_paths)
         self.assertEqual(info.include_paths, [os.path.join(folder, "include"), abs_include])
         self.assertEqual(info.lib_paths, [os.path.join(folder, "lib"), abs_lib])
         self.assertEqual(info.bin_paths, [abs_bin,
@@ -220,7 +219,6 @@ def cpp_info_libs_system_deps_order_test(self):
         info["LIB3"].lib = "lib3"
         info["LIB3"].system_deps = ["sys3", "sys2"]
         self.assertEqual(['sys2', 'lib2', 'sys3', 'lib3', 'sys1', 'sys11', 'lib1'], info.libs)
-        print(info.libs)
 
     def cppinfo_dirs_test(self):
         folder = temp_folder()

From 058ed4e434a32b719c9bc1f7e0a09629ad2fbc31 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Wed, 19 Jun 2019 11:20:09 +0200
Subject: [PATCH 17/47] Add exes test

---
 conans/test/unittests/model/build_info_test.py | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index cec1e3b6478..e51e58e8bc8 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -276,3 +276,15 @@ def cppinfo_dirs_test(self):
                          info["Crypto"].bindirs)
         self.assertEqual(["different_res", "another_res", "another_other_res"],
                          info["Crypto"].resdirs)
+
+    def cppinfo_exes_test(self):
+        info = CppInfo(None)
+        info.name = "OpenSSL"
+        info["Exe1"].exe = "the_exe1"
+        info["Exe2"].exe = "the_exe2"
+        self.assertEqual(["the_exe1", "the_exe2"], info.exes)
+
+        with six.assertRaisesRegex(self, ConanException, "Setting first level exes is not supported "
+                                                         "when Components are already in use"):
+            info.exes = ["another_exe"]
+

From 0d9ba39d0c2161a2436726500d5abaea014706cf Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Wed, 19 Jun 2019 11:33:28 +0200
Subject: [PATCH 18/47] Test wrong cpp_info raises on create

---
 conans/test/integration/package_info_test.py | 23 ++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/conans/test/integration/package_info_test.py b/conans/test/integration/package_info_test.py
index 61bd9bea3dc..d201d45fb0c 100644
--- a/conans/test/integration/package_info_test.py
+++ b/conans/test/integration/package_info_test.py
@@ -2,6 +2,9 @@
 import textwrap
 import unittest
 
+import six
+
+from conans.errors import ConanException
 from conans.model.ref import ConanFileReference, PackageReference
 from conans.paths import CONANFILE, CONANFILE_TXT
 from conans.test.utils.tools import TestClient, NO_SETTINGS_PACKAGE_ID
@@ -109,3 +112,23 @@ def build(self):
         self.assertIn("Accumulators: %s" % accumulators_expected, client.out)
         self.assertIn("Containers: %s" % containers_expected, client.out)
         self.assertIn("SuperContainers: %s" % supercontainers_expected, client.out)
+
+    def package_info_wrong_cpp_info_test(self):
+        conanfile = textwrap.dedent("""
+        import os
+        from conans import ConanFile
+
+        class Dep(ConanFile):
+
+            def package_info(self):
+                self.cpp_info.name = "Boost"
+                self.cpp_info["Accumulators"].includedirs = [os.path.join("boost", "accumulators")]
+                self.cpp_info.libs = ["hello"]
+        """)
+
+        client = TestClient()
+        client.save({"conanfile.py": conanfile})
+        client.run("export . name/1.0@us/ch")  # Does NOT fail on export
+        client.run("create . name/1.0@us/ch", assert_error=True)
+        self.assertIn("Setting first level libs is not supported when Components are already in use",
+                      client.out)

From db8b873d14eb24c00a7c510d09dc9d18e31523a2 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Wed, 19 Jun 2019 11:42:18 +0200
Subject: [PATCH 19/47] Added system_deps test

---
 conans/model/build_info.py                     |  7 ++++---
 conans/test/unittests/model/build_info_test.py | 18 ++++++++++++++++++
 2 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index e08654a16cb..66e5642a4f2 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -68,7 +68,7 @@ def libs(self, libs):
     def exes(self):
         if self._deps:
             deps = [v for v in self._deps.values()]
-            return [dep.exe for dep in deps if dep.exe is not None]
+            return [dep.exe for dep in deps if dep.exe]
         else:
             return self._exes
 
@@ -204,9 +204,10 @@ def libs(self):
             result = []
             for dep in deps_sorted:
                 for sys_dep in dep.system_deps:
-                    if sys_dep not in result:
+                    if sys_dep and sys_dep not in result:
                         result.append(sys_dep)
-                result.append(dep.lib)
+                if dep.lib:
+                    result.append(dep.lib)
             return result
         else:
             return self._libs
diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index e51e58e8bc8..a4f95842057 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -208,7 +208,25 @@ def cpp_info_libs_components_fail_test(self):
                                                          "when Components are already in use"):
             info.libs = ["libgreet"]
 
+    def cpp_info_system_test(self):
+        """
+        System deps are composed in '.libs' attribute even if there are no '.lib' in the component.
+        Also make sure None values are discarded
+        """
+        info = CppInfo(None)
+        info["LIB1"].system_deps = ["sys1", "sys11"]
+        info["LIB1"].deps = ["LIB2"]
+        info["LIB2"].system_deps = ["sys2"]
+        info["LIB1"].deps = ["LIB3"]
+        info["LIB3"].system_deps = ["sys3", "sys2"]
+        self.assertEqual(['sys2', 'sys3', 'sys1', 'sys11'], info.libs)
+        info["LIB3"].system_deps = [None, "sys2"]
+        self.assertEqual(['sys2', 'sys1', 'sys11'], info.libs)
+
     def cpp_info_libs_system_deps_order_test(self):
+        """
+        Check the order of libs and system_deps and discard repeated values
+        """
         info = CppInfo(None)
         info["LIB1"].lib = "lib1"
         info["LIB1"].system_deps = ["sys1", "sys11"]

From b098c12d0e46d469692ca08f2f05b46f32aea97a Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Wed, 19 Jun 2019 15:39:40 +0200
Subject: [PATCH 20/47] Adds complete test

---
 conans/model/build_info.py                   |  40 +++++---
 conans/test/integration/package_info_test.py | 101 +++++++++++++++++++
 2 files changed, 127 insertions(+), 14 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index 66e5642a4f2..73ffe7f7b81 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -97,21 +97,28 @@ def _filter_paths(self, paths):
     def _get_paths(self, path_name):
         """
         Get the absolute paths either composing the lists from components or from the global
-        variables. Also filter the values checking if the folders exist or not. This paths are
-        calculated once and then the result is cached.
+        variables. Also filter the values checking if the folders exist or not and avoid repeated
+        values. The paths are calculated once and then the result is cached.
         :param path_name: name of the path variable to get (include_paths, res_paths...)
         :return: List of absolute paths
         """
-        if getattr(self, "_%s_paths" % path_name) is None:
+        def get_paths_value():
+            return getattr(self, "_%s_paths" % path_name)
+
+        if get_paths_value() is None:
             if self._deps:
                 self.__dict__["_%s_paths" % path_name] = []
                 for dep_value in self._deps.values():
-                    self.__dict__["_%s_paths" % path_name].extend(
-                            self._filter_paths(getattr(dep_value, "%s_paths" % path_name)))
+                    abs_paths = self._filter_paths(getattr(dep_value, "%s_paths" % path_name))
+                    if not get_paths_value():
+                        self.__dict__["_%s_paths" % path_name].extend(abs_paths)
+                    for path in abs_paths:
+                        if path not in get_paths_value():
+                            self.__dict__["_%s_paths" % path_name].append(path)
             else:
-                self.__dict__["_%s_paths" % path_name] = self._filter_paths(
-                        getattr(self, "%sdirs" % path_name))
-        return getattr(self, "_%s_paths" % path_name)
+                abs_paths = self._filter_paths(getattr(self, "%sdirs" % path_name))
+                self.__dict__["_%s_paths" % path_name] = abs_paths
+        return get_paths_value()
 
     @property
     def include_paths(self):
@@ -228,6 +235,10 @@ def __getitem__(self, key):
             self._deps[key] = Component(key, self.rootpath)
         return self._deps[key]
 
+    @property
+    def deps(self):
+        return self._deps
+
     def __getattr__(self, config):
 
         def _get_cpp_info():
@@ -253,11 +264,11 @@ def __init__(self, name, root_folder):
         self._lib = None
         self._exe = None
         self.system_deps = []
-        self.includedirs = []
-        self.libdirs = []
-        self.resdirs = []
-        self.bindirs = []
-        self.builddirs = []
+        self.includedirs = [DEFAULT_INCLUDE]
+        self.libdirs = [DEFAULT_LIB]
+        self.resdirs = [DEFAULT_RES]
+        self.bindirs = [DEFAULT_BIN]
+        self.builddirs = [DEFAULT_BUILD]
         self.srcdirs = []
         self.defines = []
         self.cflags = []
@@ -336,7 +347,8 @@ def merge_lists(seq1, seq2):
         self.bindirs = merge_lists(self.bindirs, dep_cpp_info.bin_paths)
         self.resdirs = merge_lists(self.resdirs, dep_cpp_info.res_paths)
         self.builddirs = merge_lists(self.builddirs, dep_cpp_info.build_paths)
-        self.libs = merge_lists(self.libs, dep_cpp_info._libs)
+        self.libs = merge_lists(self.libs, dep_cpp_info.libs)
+        self.exes = merge_lists(self.exes, dep_cpp_info.exes)
         self.rootpaths.append(dep_cpp_info.rootpath)
 
         # Note these are in reverse order
diff --git a/conans/test/integration/package_info_test.py b/conans/test/integration/package_info_test.py
index d201d45fb0c..cd62385bac6 100644
--- a/conans/test/integration/package_info_test.py
+++ b/conans/test/integration/package_info_test.py
@@ -132,3 +132,104 @@ def package_info(self):
         client.run("create . name/1.0@us/ch", assert_error=True)
         self.assertIn("Setting first level libs is not supported when Components are already in use",
                       client.out)
+
+    def package_info_components_complete_test(self):
+        dep = textwrap.dedent("""
+        import os
+        from conans import ConanFile
+
+        class Dep(ConanFile):
+            exports_sources = "*"
+
+            def package(self):
+                self.copy("*")
+
+            def package_info(self):
+                self.cpp_info.name = "Galaxy"
+                self.cpp_info["Starlight"].includedirs = [os.path.join("galaxy", "starlight")]
+                self.cpp_info["Starlight"].lib = "libstarlight"
+
+                self.cpp_info["Planet"].includedirs = [os.path.join("galaxy", "planet")]
+                self.cpp_info["Planet"].lib = "libplanet"
+                self.cpp_info["Planet"].deps = ["Starlight"]
+
+                self.cpp_info["Launcher"].exe = "exelauncher"
+                self.cpp_info["Launcher"].system_deps = ["ground"]
+
+                self.cpp_info["ISS"].includedirs = [os.path.join("galaxy", "iss")]
+                self.cpp_info["ISS"].lib = "libiss"
+                self.cpp_info["ISS"].libdirs = ["iss_libs"]
+                self.cpp_info["ISS"].system_deps = ["solar", "magnetism"]
+                self.cpp_info["ISS"].deps = ["Starlight", "Launcher"]
+        """)
+        consumer = textwrap.dedent("""
+        from conans import ConanFile
+
+        class Consumer(ConanFile):
+            requires = "dep/1.0@us/ch"
+
+            def build(self):
+                # Global values
+                self.output.info("GLOBAL Include Paths: %s" % self.deps_cpp_info.include_paths)
+                self.output.info("GLOBAL Library Paths: %s" % self.deps_cpp_info.lib_paths)
+                self.output.info("GLOBAL Binary Paths: %s" % self.deps_cpp_info.bin_paths)
+                self.output.info("GLOBAL Libs: %s" % self.deps_cpp_info.libs)
+                self.output.info("GLOBAL Exes: %s" % self.deps_cpp_info.exes)
+                # Deps values
+                for dep_key, dep_value in self.deps_cpp_info.dependencies:
+                    self.output.info("DEPS Include paths: %s" % dep_value.include_paths)
+                    self.output.info("DEPS Library paths: %s" % dep_value.lib_paths)
+                    self.output.info("DEPS Binary paths: %s" % dep_value.bin_paths)
+                    self.output.info("DEPS Libs: %s" % dep_value.libs)
+                    self.output.info("DEPS Exes: %s" % dep_value.exes)
+                # Components values
+                for dep_key, dep_value in self.deps_cpp_info.dependencies:
+                    for comp_name, comp_value in dep_value.deps.items():
+                        self.output.info("COMP %s Include paths: %s" % (comp_name,
+                        comp_value.include_paths))
+                        self.output.info("COMP %s Library paths: %s" % (comp_name, comp_value.lib_paths))
+                        self.output.info("COMP %s Binary paths: %s" % (comp_name, comp_value.bin_paths))
+                        self.output.info("COMP %s Lib: %s" % (comp_name, comp_value.lib))
+                        self.output.info("COMP %s Exe: %s" % (comp_name, comp_value.exe))
+                        self.output.info("COMP %s Deps: %s" % (comp_name, comp_value.deps))
+        """)
+
+        client = TestClient()
+        client.save({"conanfile_dep.py": dep, "conanfile_consumer.py": consumer,
+                     "galaxy/starlight/starlight.h": "",
+                     "lib/libstarlight": "",
+                     "galaxy/planet/planet.h": "",
+                     "lib/libplanet": "",
+                     "galaxy/iss/iss.h": "",
+                     "iss_libs/libiss": "",
+                     "bin/exelauncher": ""})
+        dep_ref = ConanFileReference("dep", "1.0", "us", "ch")
+        dep_pref = PackageReference(dep_ref, NO_SETTINGS_PACKAGE_ID)
+        client.run("create conanfile_dep.py dep/1.0@us/ch")
+        client.run("create conanfile_consumer.py consumer/1.0@us/ch")
+        package_folder = client.cache.package_layout(dep_ref).package(dep_pref)
+
+        expected_global_include_paths = [os.path.join(package_folder, "galaxy", "starlight"),
+                                         os.path.join(package_folder, "galaxy", "planet"),
+                                         os.path.join(package_folder, "galaxy", "iss")]
+        expected_global_library_paths = [os.path.join(package_folder, "lib"),
+                                         os.path.join(package_folder, "iss_libs")]
+        expected_global_binary_paths = [os.path.join(package_folder, "bin")]
+        expected_global_libs = ["libstarlight", "ground", "libplanet", "solar", "magnetism", "libiss"]
+        expected_global_exes = ["exelauncher"]
+        fromatted1 = "GLOBAL Include Paths: %s" % expected_global_include_paths
+        self.assertIn(fromatted1, client.out)
+        self.assertIn("GLOBAL Library Paths: %s" % expected_global_library_paths, client.out)
+        self.assertIn("GLOBAL Binary Paths: %s" % expected_global_binary_paths, client.out)
+        self.assertIn("GLOBAL Libs: %s" % expected_global_libs, client.out)
+        self.assertIn("GLOBAL Exes: %s" % expected_global_exes, client.out)
+
+        formatted = "GLOBAL Include Paths: {}".format(expected_global_include_paths)
+        print(formatted)
+        self.assertIn(formatted, client.out)
+        self.assertIn("DEPS Library Paths: %s" % expected_global_library_paths, client.out)
+        self.assertIn("DEPS Binary Paths: %s" % expected_global_binary_paths, client.out)
+        self.assertIn("DEPS Libs: %s" % expected_global_libs, client.out)
+        self.assertIn("DEPS Exes: %s" % expected_global_exes, client.out)
+
+        #TODO: Complete the test checking the output of the components

From 6ab1bdbeb8c3858b1922ee2cdb56bc9a25ab8334 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Wed, 19 Jun 2019 15:52:04 +0200
Subject: [PATCH 21/47] Add system_deps global behavior

---
 conans/model/build_info.py                    | 24 ++++++++++++++++++-
 .../test/unittests/model/build_info_test.py   | 12 ++++++++--
 2 files changed, 33 insertions(+), 3 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index 73ffe7f7b81..c34385bd51d 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -20,7 +20,7 @@ class _CppInfo(object):
     """
     def __init__(self):
         self.name = None
-        self.system_deps = []
+        self._system_deps = []
         self.includedirs = []  # Ordered list of include paths
         self.srcdirs = []  # Ordered list of source paths
         self.libdirs = []  # Directories to find libraries
@@ -79,6 +79,28 @@ def exes(self, exes):
                                  "already in use")
         self._exes = exes
 
+    @property
+    def system_deps(self):
+        if self._deps:
+            deps = [v for v in self._deps.values()]
+            deps_sorted = sorted(deps, key=lambda component: len(component.deps))
+            result = []
+            for dep in deps_sorted:
+                if dep.system_deps:
+                    for system_dep in dep.system_deps:
+                        if system_dep and system_dep not in result:
+                            result.append(system_dep)
+            return result
+        else:
+            return self._system_deps
+
+    @system_deps.setter
+    def system_deps(self, system_deps):
+        if self._deps:
+            raise ConanException("Setting first level system_deps is not supported when Components "
+                                 "are already in use")
+        self._system_deps = system_deps
+
     def __getitem__(self, key):
         if self._libs:
             raise ConanException("Usage of Components with '.libs' values is not allowed")
diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index a4f95842057..b81094dc86f 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -208,10 +208,11 @@ def cpp_info_libs_components_fail_test(self):
                                                          "when Components are already in use"):
             info.libs = ["libgreet"]
 
-    def cpp_info_system_test(self):
+    def cpp_info_system_deps_test(self):
         """
         System deps are composed in '.libs' attribute even if there are no '.lib' in the component.
-        Also make sure None values are discarded
+        Also make sure None values are discarded.
+        Same for '.system_deps' making sure that mixing Components and global use is not supported.
         """
         info = CppInfo(None)
         info["LIB1"].system_deps = ["sys1", "sys11"]
@@ -223,6 +224,12 @@ def cpp_info_system_test(self):
         info["LIB3"].system_deps = [None, "sys2"]
         self.assertEqual(['sys2', 'sys1', 'sys11'], info.libs)
 
+        with six.assertRaisesRegex(self, ConanException, "Setting first level system_deps is not "
+                                                         "supported when Components are already in "
+                                                         "use"):
+            info.system_deps = ["random_system"]
+        self.assertEqual(['sys2', 'sys1', 'sys11'], info.system_deps)
+
     def cpp_info_libs_system_deps_order_test(self):
         """
         Check the order of libs and system_deps and discard repeated values
@@ -237,6 +244,7 @@ def cpp_info_libs_system_deps_order_test(self):
         info["LIB3"].lib = "lib3"
         info["LIB3"].system_deps = ["sys3", "sys2"]
         self.assertEqual(['sys2', 'lib2', 'sys3', 'lib3', 'sys1', 'sys11', 'lib1'], info.libs)
+        self.assertEqual(['sys2', 'sys3', 'sys1', 'sys11'], info.system_deps)
 
     def cppinfo_dirs_test(self):
         folder = temp_folder()

From d1d2ffc8e7eaec85b2eee81602ddbf8f26c126ba Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Thu, 20 Jun 2019 12:34:20 +0200
Subject: [PATCH 22/47] Indent text

---
 conans/test/integration/package_info_test.py | 129 +++++++++----------
 1 file changed, 63 insertions(+), 66 deletions(-)

diff --git a/conans/test/integration/package_info_test.py b/conans/test/integration/package_info_test.py
index cd62385bac6..ae357ab9325 100644
--- a/conans/test/integration/package_info_test.py
+++ b/conans/test/integration/package_info_test.py
@@ -55,42 +55,42 @@ def package_info(self):
 
     def package_info_components_test(self):
         dep = textwrap.dedent("""
-        import os
-        from conans import ConanFile
-
-        class Dep(ConanFile):
-            exports_sources = "*"
-
-            def package(self):
-                self.copy("*")
-
-            def package_info(self):
-                self.cpp_info.name = "Boost"
-                self.cpp_info["Accumulators"].includedirs = [os.path.join("boost", "accumulators")]
-                self.cpp_info["Accumulators"].lib = "libaccumulators"
-                self.cpp_info["Containers"].includedirs = [os.path.join("boost", "containers")]
-                self.cpp_info["Containers"].lib = "libcontainers"
-                self.cpp_info["Containers"].deps = ["Accumulators"]
-                self.cpp_info["SuperContainers"].includedirs = [os.path.join("boost",
-                                                                             "supercontainers")]
-                self.cpp_info["SuperContainers"].lib = "libsupercontainers"
-                self.cpp_info["SuperContainers"].deps = ["Containers"]
+            import os
+            from conans import ConanFile
+
+            class Dep(ConanFile):
+                exports_sources = "*"
+
+                def package(self):
+                    self.copy("*")
+
+                def package_info(self):
+                    self.cpp_info.name = "Boost"
+                    self.cpp_info["Accumulators"].includedirs = [os.path.join("boost", "accumulators")]
+                    self.cpp_info["Accumulators"].lib = "libaccumulators"
+                    self.cpp_info["Containers"].includedirs = [os.path.join("boost", "containers")]
+                    self.cpp_info["Containers"].lib = "libcontainers"
+                    self.cpp_info["Containers"].deps = ["Accumulators"]
+                    self.cpp_info["SuperContainers"].includedirs = [os.path.join("boost",
+                                                                                 "supercontainers")]
+                    self.cpp_info["SuperContainers"].lib = "libsupercontainers"
+                    self.cpp_info["SuperContainers"].deps = ["Containers"]
         """)
         consumer = textwrap.dedent("""
-        from conans import ConanFile
-
-        class Consumer(ConanFile):
-            requires = "dep/1.0@us/ch"
-
-            def build(self):
-                acc_includes = self.deps_cpp_info["dep"]["Accumulators"].include_paths
-                con_include = self.deps_cpp_info["dep"]["Containers"].include_paths
-                sup_include = self.deps_cpp_info["dep"]["SuperContainers"].include_paths
-                self.output.info("Name: %s" % self.deps_cpp_info["dep"].name)
-                self.output.info("Accumulators: %s" % acc_includes)
-                self.output.info("Containers: %s" % con_include)
-                self.output.info("SuperContainers: %s" % sup_include)
-                self.output.info("LIBS: %s" % self.deps_cpp_info["dep"].libs)
+            from conans import ConanFile
+
+            class Consumer(ConanFile):
+                requires = "dep/1.0@us/ch"
+
+                def build(self):
+                    acc_includes = self.deps_cpp_info["dep"]["Accumulators"].include_paths
+                    con_include = self.deps_cpp_info["dep"]["Containers"].include_paths
+                    sup_include = self.deps_cpp_info["dep"]["SuperContainers"].include_paths
+                    self.output.info("Name: %s" % self.deps_cpp_info["dep"].name)
+                    self.output.info("Accumulators: %s" % acc_includes)
+                    self.output.info("Containers: %s" % con_include)
+                    self.output.info("SuperContainers: %s" % sup_include)
+                    self.output.info("LIBS: %s" % self.deps_cpp_info["dep"].libs)
         """)
 
         client = TestClient()
@@ -115,15 +115,15 @@ def build(self):
 
     def package_info_wrong_cpp_info_test(self):
         conanfile = textwrap.dedent("""
-        import os
-        from conans import ConanFile
+            import os
+            from conans import ConanFile
 
-        class Dep(ConanFile):
+            class Dep(ConanFile):
 
-            def package_info(self):
-                self.cpp_info.name = "Boost"
-                self.cpp_info["Accumulators"].includedirs = [os.path.join("boost", "accumulators")]
-                self.cpp_info.libs = ["hello"]
+                def package_info(self):
+                    self.cpp_info.name = "Boost"
+                    self.cpp_info["Accumulators"].includedirs = [os.path.join("boost", "accumulators")]
+                    self.cpp_info.libs = ["hello"]
         """)
 
         client = TestClient()
@@ -135,32 +135,32 @@ def package_info(self):
 
     def package_info_components_complete_test(self):
         dep = textwrap.dedent("""
-        import os
-        from conans import ConanFile
+            import os
+            from conans import ConanFile
 
-        class Dep(ConanFile):
-            exports_sources = "*"
+            class Dep(ConanFile):
+                exports_sources = "*"
 
-            def package(self):
-                self.copy("*")
+                def package(self):
+                    self.copy("*")
 
-            def package_info(self):
-                self.cpp_info.name = "Galaxy"
-                self.cpp_info["Starlight"].includedirs = [os.path.join("galaxy", "starlight")]
-                self.cpp_info["Starlight"].lib = "libstarlight"
+                def package_info(self):
+                    self.cpp_info.name = "Galaxy"
+                    self.cpp_info["Starlight"].includedirs = [os.path.join("galaxy", "starlight")]
+                    self.cpp_info["Starlight"].lib = "libstarlight"
 
-                self.cpp_info["Planet"].includedirs = [os.path.join("galaxy", "planet")]
-                self.cpp_info["Planet"].lib = "libplanet"
-                self.cpp_info["Planet"].deps = ["Starlight"]
+                    self.cpp_info["Planet"].includedirs = [os.path.join("galaxy", "planet")]
+                    self.cpp_info["Planet"].lib = "libplanet"
+                    self.cpp_info["Planet"].deps = ["Starlight"]
 
-                self.cpp_info["Launcher"].exe = "exelauncher"
-                self.cpp_info["Launcher"].system_deps = ["ground"]
+                    self.cpp_info["Launcher"].exe = "exelauncher"
+                    self.cpp_info["Launcher"].system_deps = ["ground"]
 
-                self.cpp_info["ISS"].includedirs = [os.path.join("galaxy", "iss")]
-                self.cpp_info["ISS"].lib = "libiss"
-                self.cpp_info["ISS"].libdirs = ["iss_libs"]
-                self.cpp_info["ISS"].system_deps = ["solar", "magnetism"]
-                self.cpp_info["ISS"].deps = ["Starlight", "Launcher"]
+                    self.cpp_info["ISS"].includedirs = [os.path.join("galaxy", "iss")]
+                    self.cpp_info["ISS"].lib = "libiss"
+                    self.cpp_info["ISS"].libdirs = ["iss_libs"]
+                    self.cpp_info["ISS"].system_deps = ["solar", "magnetism"]
+                    self.cpp_info["ISS"].deps = ["Starlight", "Launcher"]
         """)
         consumer = textwrap.dedent("""
         from conans import ConanFile
@@ -217,16 +217,13 @@ def build(self):
         expected_global_binary_paths = [os.path.join(package_folder, "bin")]
         expected_global_libs = ["libstarlight", "ground", "libplanet", "solar", "magnetism", "libiss"]
         expected_global_exes = ["exelauncher"]
-        fromatted1 = "GLOBAL Include Paths: %s" % expected_global_include_paths
-        self.assertIn(fromatted1, client.out)
+        self.assertIn("GLOBAL Include Paths: %s" % expected_global_include_paths, client.out)
         self.assertIn("GLOBAL Library Paths: %s" % expected_global_library_paths, client.out)
         self.assertIn("GLOBAL Binary Paths: %s" % expected_global_binary_paths, client.out)
         self.assertIn("GLOBAL Libs: %s" % expected_global_libs, client.out)
         self.assertIn("GLOBAL Exes: %s" % expected_global_exes, client.out)
 
-        formatted = "GLOBAL Include Paths: {}".format(expected_global_include_paths)
-        print(formatted)
-        self.assertIn(formatted, client.out)
+        self.assertIn("DEPS Include Paths: {}".format(expected_global_include_paths), client.out)
         self.assertIn("DEPS Library Paths: %s" % expected_global_library_paths, client.out)
         self.assertIn("DEPS Binary Paths: %s" % expected_global_binary_paths, client.out)
         self.assertIn("DEPS Libs: %s" % expected_global_libs, client.out)

From be74c0d83438de4accc1166495187b40048196cd Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Thu, 20 Jun 2019 12:35:13 +0200
Subject: [PATCH 23/47] Review and move libs properties. Use function to get
 components sorted

---
 conans/model/build_info.py | 76 ++++++++++++++------------------------
 1 file changed, 27 insertions(+), 49 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index c34385bd51d..73a43c60a88 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -49,13 +49,27 @@ def __init__(self):
         self.filter_empty = True
         self._deps = OrderedDict()
 
+    def _get_components_sorted(self):
+        """
+        Sort components from less dependent to the most one (less items in .deps attribute)
+        :return: ordered list of components
+        """
+        components = [v for v in self._deps.values()]
+        return sorted(components, key=lambda component: len(component.deps))
+
     @property
     def libs(self):
         if self._deps:
-            deps = [v for v in self._deps.values()]
-            deps_sorted = sorted(deps, key=lambda component: len(component.deps))
-            self._libs = [dep.lib for dep in deps_sorted if dep.lib is not None]
-        return self._libs
+            result = []
+            for component in self._get_components_sorted():
+                for sys_dep in component.system_deps:
+                    if sys_dep and sys_dep not in result:
+                        result.append(sys_dep)
+                if component.lib:
+                    result.append(component.lib)
+            return result
+        else:
+            return self._libs
 
     @libs.setter
     def libs(self, libs):
@@ -67,8 +81,7 @@ def libs(self, libs):
     @property
     def exes(self):
         if self._deps:
-            deps = [v for v in self._deps.values()]
-            return [dep.exe for dep in deps if dep.exe]
+            return [component.exe for component in self._deps.values() if component.exe]
         else:
             return self._exes
 
@@ -82,12 +95,10 @@ def exes(self, exes):
     @property
     def system_deps(self):
         if self._deps:
-            deps = [v for v in self._deps.values()]
-            deps_sorted = sorted(deps, key=lambda component: len(component.deps))
             result = []
-            for dep in deps_sorted:
-                if dep.system_deps:
-                    for system_dep in dep.system_deps:
+            for component in self._get_components_sorted():
+                if component.system_deps:
+                    for system_dep in component.system_deps:
                         if system_dep and system_dep not in result:
                             result.append(system_dep)
             return result
@@ -109,8 +120,7 @@ def __getitem__(self, key):
         return self._deps[key]
 
     def _filter_paths(self, paths):
-        abs_paths = [os.path.join(self.rootpath, p)
-                     if not os.path.isabs(p) else p for p in paths]
+        abs_paths = [os.path.join(self.rootpath, p) for p in paths]
         if self.filter_empty:
             return [p for p in abs_paths if os.path.isdir(p)]
         else:
@@ -207,7 +217,7 @@ def _check_dirs_values(self):
             "srcdirs": []
         }
         msg_template = "Using Components and global '{}' values ('{}') is not supported"
-        for dir_name in ["includedirs", "libdirs", "bindirs", "builddirs"]:
+        for dir_name in default_dirs_mapping:
             dirs_value = getattr(self, dir_name)
             if dirs_value is not None and dirs_value != default_dirs_mapping[dir_name]:
                 raise ConanException(msg_template.format(dir_name, dirs_value))
@@ -221,33 +231,10 @@ def _clear_dirs_values(self):
             "builddirs": [DEFAULT_BUILD],
             "srcdirs": []
         }
-        for dir_name in ["includedirs", "libdirs", "bindirs", "builddirs"]:
+        for dir_name in default_dirs_mapping:
             if getattr(self, dir_name) == default_dirs_mapping[dir_name]:
                 self.__dict__[dir_name] = None
 
-    @property
-    def libs(self):
-        if self._deps:
-            deps = [v for v in self._deps.values()]
-            deps_sorted = sorted(deps, key=lambda component: len(component.deps))
-            result = []
-            for dep in deps_sorted:
-                for sys_dep in dep.system_deps:
-                    if sys_dep and sys_dep not in result:
-                        result.append(sys_dep)
-                if dep.lib:
-                    result.append(dep.lib)
-            return result
-        else:
-            return self._libs
-
-    @libs.setter
-    def libs(self, libs):
-        if self._deps:
-            raise ConanException("Setting first level libs is not supported when Components are "
-                                 "already in use")
-        self._libs = libs
-
     def __getitem__(self, key):
         if self._libs or self._exes:
             raise ConanException("Usage of Components with '.libs' or '.exes' values is not allowed")
@@ -271,7 +258,7 @@ def _get_cpp_info():
             result.libdirs.append(DEFAULT_LIB)
             result.bindirs.append(DEFAULT_BIN)
             result.resdirs.append(DEFAULT_RES)
-            result.builddirs.append("")
+            result.builddirs.append(DEFAULT_BUILD)
             return result
 
         return self.configs.setdefault(config, _get_cpp_info())
@@ -301,8 +288,7 @@ def __init__(self, name, root_folder):
         self._filter_empty = True
 
     def _filter_paths(self, paths):
-        abs_paths = [os.path.join(self._rootpath, p)
-                     if not os.path.isabs(p) else p for p in paths]
+        abs_paths = [os.path.join(self._rootpath, p) for p in paths]
         if self._filter_empty:
             return [p for p in abs_paths if os.path.isdir(p)]
         else:
@@ -383,14 +369,6 @@ def merge_lists(seq1, seq2):
         if not self.sysroot:
             self.sysroot = dep_cpp_info.sysroot
 
-    @property
-    def libs(self):
-        return self._libs
-
-    @libs.setter
-    def libs(self, libs):
-        self._libs = libs
-
     @property
     def include_paths(self):
         return self.includedirs

From 1e6e1fdafd8b7eb3e106aedfa48c97257789ee9e Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Thu, 20 Jun 2019 12:49:13 +0200
Subject: [PATCH 24/47] Renamed .deps to .components

---
 conans/model/build_info.py                   | 49 ++++++++++----------
 conans/test/integration/package_info_test.py |  2 +-
 2 files changed, 25 insertions(+), 26 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index 73a43c60a88..0544a56c69a 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -47,21 +47,21 @@ def __init__(self):
         self.description = None  # Description of the conan package
         # When package is editable, filter_empty=False, so empty dirs are maintained
         self.filter_empty = True
-        self._deps = OrderedDict()
+        self._components = OrderedDict()
 
-    def _get_components_sorted(self):
+    @property
+    def _sorted_components(self):
         """
-        Sort components from less dependent to the most one (less items in .deps attribute)
+        Sorted components from less dependent to the most one (less items in .deps attribute)
         :return: ordered list of components
         """
-        components = [v for v in self._deps.values()]
-        return sorted(components, key=lambda component: len(component.deps))
+        return sorted(self._components.values(), key=lambda component: len(component.deps))
 
     @property
     def libs(self):
-        if self._deps:
+        if self._components:
             result = []
-            for component in self._get_components_sorted():
+            for component in self._sorted_components:
                 for sys_dep in component.system_deps:
                     if sys_dep and sys_dep not in result:
                         result.append(sys_dep)
@@ -73,30 +73,30 @@ def libs(self):
 
     @libs.setter
     def libs(self, libs):
-        if self._deps:
+        if self._components:
             raise ConanException("Setting first level libs is not supported when Components are "
                                  "already in use")
         self._libs = libs
 
     @property
     def exes(self):
-        if self._deps:
-            return [component.exe for component in self._deps.values() if component.exe]
+        if self._components:
+            return [component.exe for component in self._components.values() if component.exe]
         else:
             return self._exes
 
     @exes.setter
     def exes(self, exes):
-        if self._deps:
+        if self._components:
             raise ConanException("Setting first level exes is not supported when Components are "
                                  "already in use")
         self._exes = exes
 
     @property
     def system_deps(self):
-        if self._deps:
+        if self._components:
             result = []
-            for component in self._get_components_sorted():
+            for component in self._sorted_components:
                 if component.system_deps:
                     for system_dep in component.system_deps:
                         if system_dep and system_dep not in result:
@@ -107,7 +107,7 @@ def system_deps(self):
 
     @system_deps.setter
     def system_deps(self, system_deps):
-        if self._deps:
+        if self._components:
             raise ConanException("Setting first level system_deps is not supported when Components "
                                  "are already in use")
         self._system_deps = system_deps
@@ -115,9 +115,9 @@ def system_deps(self, system_deps):
     def __getitem__(self, key):
         if self._libs:
             raise ConanException("Usage of Components with '.libs' values is not allowed")
-        if key not in self._deps.keys():
-            self._deps[key] = Component(self, key)
-        return self._deps[key]
+        if key not in self._components.keys():
+            self._components[key] = Component(self, key)
+        return self._components[key]
 
     def _filter_paths(self, paths):
         abs_paths = [os.path.join(self.rootpath, p) for p in paths]
@@ -138,9 +138,9 @@ def get_paths_value():
             return getattr(self, "_%s_paths" % path_name)
 
         if get_paths_value() is None:
-            if self._deps:
+            if self._components:
                 self.__dict__["_%s_paths" % path_name] = []
-                for dep_value in self._deps.values():
+                for dep_value in self._components.values():
                     abs_paths = self._filter_paths(getattr(dep_value, "%s_paths" % path_name))
                     if not get_paths_value():
                         self.__dict__["_%s_paths" % path_name].extend(abs_paths)
@@ -205,7 +205,6 @@ def __init__(self, root_folder):
         # public_deps is needed to accumulate list of deps for cmake targets
         self.public_deps = []
         self.configs = {}
-        self._deps = OrderedDict()
 
     def _check_dirs_values(self):
         default_dirs_mapping = {
@@ -240,13 +239,13 @@ def __getitem__(self, key):
             raise ConanException("Usage of Components with '.libs' or '.exes' values is not allowed")
         self._clear_dirs_values()
         self._check_dirs_values()
-        if key not in self._deps.keys():
-            self._deps[key] = Component(key, self.rootpath)
-        return self._deps[key]
+        if key not in self._components:
+            self._components[key] = Component(key, self.rootpath)
+        return self._components[key]
 
     @property
-    def deps(self):
-        return self._deps
+    def components(self):
+        return self._components
 
     def __getattr__(self, config):
 
diff --git a/conans/test/integration/package_info_test.py b/conans/test/integration/package_info_test.py
index ae357ab9325..14495ffa16b 100644
--- a/conans/test/integration/package_info_test.py
+++ b/conans/test/integration/package_info_test.py
@@ -184,7 +184,7 @@ def build(self):
                     self.output.info("DEPS Exes: %s" % dep_value.exes)
                 # Components values
                 for dep_key, dep_value in self.deps_cpp_info.dependencies:
-                    for comp_name, comp_value in dep_value.deps.items():
+                    for comp_name, comp_value in dep_value.components.items():
                         self.output.info("COMP %s Include paths: %s" % (comp_name,
                         comp_value.include_paths))
                         self.output.info("COMP %s Library paths: %s" % (comp_name, comp_value.lib_paths))

From 6a2e2f7eda360f6748d9facb64d11c3d924a8a79 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Thu, 20 Jun 2019 13:24:30 +0200
Subject: [PATCH 25/47] simplified get paths

---
 conans/model/build_info.py | 56 ++++++++++++++++++++++----------------
 1 file changed, 32 insertions(+), 24 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index 0544a56c69a..7350e4308d9 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -130,51 +130,59 @@ def _get_paths(self, path_name):
         """
         Get the absolute paths either composing the lists from components or from the global
         variables. Also filter the values checking if the folders exist or not and avoid repeated
-        values. The paths are calculated once and then the result is cached.
+        values.
         :param path_name: name of the path variable to get (include_paths, res_paths...)
         :return: List of absolute paths
         """
-        def get_paths_value():
-            return getattr(self, "_%s_paths" % path_name)
-
-        if get_paths_value() is None:
-            if self._components:
-                self.__dict__["_%s_paths" % path_name] = []
-                for dep_value in self._components.values():
-                    abs_paths = self._filter_paths(getattr(dep_value, "%s_paths" % path_name))
-                    if not get_paths_value():
-                        self.__dict__["_%s_paths" % path_name].extend(abs_paths)
-                    for path in abs_paths:
-                        if path not in get_paths_value():
-                            self.__dict__["_%s_paths" % path_name].append(path)
-            else:
-                abs_paths = self._filter_paths(getattr(self, "%sdirs" % path_name))
-                self.__dict__["_%s_paths" % path_name] = abs_paths
-        return get_paths_value()
+        result = []
+
+        if self._components:
+            for dep_value in self._components.values():
+                abs_paths = self._filter_paths(getattr(dep_value, "%s_paths" % path_name))
+                if not getattr(self, "_%s_paths" % path_name):
+                    result.extend(abs_paths)
+                for path in abs_paths:
+                    if path not in result:
+                        result.append(path)
+        else:
+            result = self._filter_paths(getattr(self, "%sdirs" % path_name))
+        return result
 
     @property
     def include_paths(self):
-        return self._get_paths("include")
+        if not self._include_paths:
+            self._include_paths = self._get_paths("include")
+        return self._include_paths
 
     @property
     def lib_paths(self):
-        return self._get_paths("lib")
+        if not self._lib_paths:
+            self._lib_paths = self._get_paths("lib")
+        return self._lib_paths
 
     @property
     def src_paths(self):
-        return self._get_paths("src")
+        if not self._src_paths:
+            self._src_paths = self._get_paths("src")
+        return self._src_paths
 
     @property
     def bin_paths(self):
-        return self._get_paths("bin")
+        if not self._bin_paths:
+            self._bin_paths = self._get_paths("bin")
+        return self._bin_paths
 
     @property
     def build_paths(self):
-        return self._get_paths("build")
+        if not self._build_paths:
+            self._build_paths = self._get_paths("build")
+        return self._build_paths
 
     @property
     def res_paths(self):
-        return self._get_paths("res")
+        if not self._res_paths:
+            self._res_paths = self._get_paths("res")
+        return self._res_paths
 
     # Compatibility for 'cppflags' (old style property to allow decoration)
     @deprecation.deprecated(deprecated_in="1.13", removed_in="2.0", details="Use 'cxxflags' instead")

From b32e3b9785d97a01d55c6c1ff1e95715ed09dc6f Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Thu, 20 Jun 2019 17:38:07 +0200
Subject: [PATCH 26/47] Fix link order and added test

---
 conans/model/build_info.py                    | 20 +++++++-
 conans/test/integration/package_info_test.py  |  2 +-
 .../test/unittests/model/build_info_test.py   | 50 +++++++++++++++++++
 3 files changed, 69 insertions(+), 3 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index 7350e4308d9..f98f39980f7 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -52,10 +52,26 @@ def __init__(self):
     @property
     def _sorted_components(self):
         """
-        Sorted components from less dependent to the most one (less items in .deps attribute)
+        Sorted components from less dependent to the most one
         :return: ordered list of components
         """
-        return sorted(self._components.values(), key=lambda component: len(component.deps))
+        # Sort first elements with less items in .deps attribute
+        comps = sorted(self._components.values(), key=lambda component: len(component.deps))
+        # Save name of unsorted elements
+        unsorted_names = [comp.name for comp in comps]
+
+        sorted_comps = []
+        while unsorted_names:
+            for comp in comps:
+                # If element is already sorted, continue
+                if comp.name not in unsorted_names:
+                    continue
+                # If element does not have deps or all of its deps are already sorted, sort this
+                # element and remove it from the unsorted list
+                elif not comp.deps or not [dep for dep in comp.deps if dep in unsorted_names]:
+                    sorted_comps.append(comp)
+                    unsorted_names.remove(comp.name)
+        return sorted_comps
 
     @property
     def libs(self):
diff --git a/conans/test/integration/package_info_test.py b/conans/test/integration/package_info_test.py
index 14495ffa16b..a142abd1070 100644
--- a/conans/test/integration/package_info_test.py
+++ b/conans/test/integration/package_info_test.py
@@ -223,7 +223,7 @@ def build(self):
         self.assertIn("GLOBAL Libs: %s" % expected_global_libs, client.out)
         self.assertIn("GLOBAL Exes: %s" % expected_global_exes, client.out)
 
-        self.assertIn("DEPS Include Paths: {}".format(expected_global_include_paths), client.out)
+        self.assertIn("DEPS Include Paths: %s" % expected_global_include_paths, client.out)
         self.assertIn("DEPS Library Paths: %s" % expected_global_library_paths, client.out)
         self.assertIn("DEPS Binary Paths: %s" % expected_global_binary_paths, client.out)
         self.assertIn("DEPS Libs: %s" % expected_global_libs, client.out)
diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index b81094dc86f..2db051e84d6 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -246,6 +246,56 @@ def cpp_info_libs_system_deps_order_test(self):
         self.assertEqual(['sys2', 'lib2', 'sys3', 'lib3', 'sys1', 'sys11', 'lib1'], info.libs)
         self.assertEqual(['sys2', 'sys3', 'sys1', 'sys11'], info.system_deps)
 
+    def cpp_info_link_order_test(self):
+
+        def _assert_link_order(sorted_libs):
+            for num, lib in enumerate(sorted_libs):
+                component_name = lib[-1]
+                for dep in info[component_name].deps:
+                    self.assertIn(info[dep].lib, sorted_libs[:num])
+
+        info = CppInfo(None)
+        info["F"].lib = "libF"
+        info["F"].deps = ["D", "E"]
+        info["E"].lib = "libE"
+        info["E"].deps = ["B"]
+        info["D"].lib = "libD"
+        info["D"].deps = ["A"]
+        info["C"].lib = "libC"
+        info["C"].deps = ["A"]
+        info["A"].lib = "libA"
+        info["A"].deps = ["B"]
+        info["B"].lib = "libB"
+        info["B"].deps = []
+        _assert_link_order(info.libs)
+
+        info = CppInfo(None)
+        info["K"].lib = "libK"
+        info["K"].deps = ["G", "H"]
+        info["J"].lib = "libJ"
+        info["J"].deps = ["F"]
+        info["G"].lib = "libG"
+        info["G"].deps = ["F"]
+        info["H"].lib = "libH"
+        info["H"].deps = ["F", "E"]
+        info["L"].lib = "libL"
+        info["L"].deps = ["I"]
+        info["F"].lib = "libF"
+        info["F"].deps = ["C", "D"]
+        info["I"].lib = "libI"
+        info["I"].deps = ["E"]
+        info["C"].lib = "libC"
+        info["C"].deps = ["A"]
+        info["D"].lib = "libD"
+        info["D"].deps = ["A"]
+        info["E"].lib = "libE"
+        info["E"].deps = ["A", "B"]
+        info["A"].lib = "libA"
+        info["A"].deps = []
+        info["B"].lib = "libB"
+        info["B"].deps = []
+        _assert_link_order(info.libs)
+
     def cppinfo_dirs_test(self):
         folder = temp_folder()
         info = CppInfo(folder)

From 4bb2db219cb2cc8643de142e3ac6cb6ac5b20549 Mon Sep 17 00:00:00 2001
From: Daniel <danimanzaneque@gmail.com>
Date: Thu, 20 Jun 2019 17:42:57 +0200
Subject: [PATCH 27/47] Update conans/test/unittests/model/build_info_test.py

Co-Authored-By: Javier G. Sogo <jgsogo@gmail.com>
---
 conans/test/unittests/model/build_info_test.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index 2db051e84d6..26323be8d0b 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -179,7 +179,7 @@ def cpp_info_test(self):
     def basic_components_test(self):
         cpp_info = CppInfo(None)
         component = cpp_info["my_component"]
-        self.assertIn(component.name, "my_component")
+        self.assertEqual(component.name, "my_component")
         component.lib = "libhola"
         self.assertEqual(component.lib, "libhola")
         with six.assertRaisesRegex(self, ConanException, "'.lib' is already set for this Component"):

From 4b490268ca7e70ad86fa56907cf39056b5ef00dd Mon Sep 17 00:00:00 2001
From: Daniel <danimanzaneque@gmail.com>
Date: Thu, 20 Jun 2019 17:43:08 +0200
Subject: [PATCH 28/47] Update conans/test/unittests/model/build_info_test.py

Co-Authored-By: Javier G. Sogo <jgsogo@gmail.com>
---
 conans/test/unittests/model/build_info_test.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index 26323be8d0b..c0f6ea26e69 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -196,7 +196,7 @@ def cpp_info_libs_components_fail_test(self):
         """
         info = CppInfo(None)
         info.name = "Greetings"
-        self.assertIn(info.name, "Greetings")
+        self.assertEqual(info.name, "Greetings")
         info.libs = ["libgreet"]
         with six.assertRaisesRegex(self, ConanException, "Usage of Components with '.libs' or "
                                                          "'.exes' values is not allowed"):

From 62a9782b959bf6829df4dd513e53443d82df6f98 Mon Sep 17 00:00:00 2001
From: Daniel <danimanzaneque@gmail.com>
Date: Thu, 20 Jun 2019 17:53:13 +0200
Subject: [PATCH 29/47] Update conans/test/integration/package_info_test.py

Co-Authored-By: Javier G. Sogo <jgsogo@gmail.com>
---
 conans/test/integration/package_info_test.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/conans/test/integration/package_info_test.py b/conans/test/integration/package_info_test.py
index a142abd1070..ecb639d655b 100644
--- a/conans/test/integration/package_info_test.py
+++ b/conans/test/integration/package_info_test.py
@@ -224,7 +224,7 @@ def build(self):
         self.assertIn("GLOBAL Exes: %s" % expected_global_exes, client.out)
 
         self.assertIn("DEPS Include Paths: %s" % expected_global_include_paths, client.out)
-        self.assertIn("DEPS Library Paths: %s" % expected_global_library_paths, client.out)
+        self.assertIn("DEPS Library paths: %s" % expected_global_library_paths, client.out)
         self.assertIn("DEPS Binary Paths: %s" % expected_global_binary_paths, client.out)
         self.assertIn("DEPS Libs: %s" % expected_global_libs, client.out)
         self.assertIn("DEPS Exes: %s" % expected_global_exes, client.out)

From b756503bee5cbf6fa3ac0eccb94de279595351f2 Mon Sep 17 00:00:00 2001
From: Daniel <danimanzaneque@gmail.com>
Date: Thu, 20 Jun 2019 17:53:22 +0200
Subject: [PATCH 30/47] Update conans/test/integration/package_info_test.py

Co-Authored-By: Javier G. Sogo <jgsogo@gmail.com>
---
 conans/test/integration/package_info_test.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/conans/test/integration/package_info_test.py b/conans/test/integration/package_info_test.py
index ecb639d655b..98ea92998fb 100644
--- a/conans/test/integration/package_info_test.py
+++ b/conans/test/integration/package_info_test.py
@@ -225,7 +225,7 @@ def build(self):
 
         self.assertIn("DEPS Include Paths: %s" % expected_global_include_paths, client.out)
         self.assertIn("DEPS Library paths: %s" % expected_global_library_paths, client.out)
-        self.assertIn("DEPS Binary Paths: %s" % expected_global_binary_paths, client.out)
+        self.assertIn("DEPS Binary paths: %s" % expected_global_binary_paths, client.out)
         self.assertIn("DEPS Libs: %s" % expected_global_libs, client.out)
         self.assertIn("DEPS Exes: %s" % expected_global_exes, client.out)
 

From aef1a51a2bc46a98e035c1753bdeec795d9ffaac Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Thu, 20 Jun 2019 18:15:07 +0200
Subject: [PATCH 31/47] asserts

---
 conans/model/build_info.py                     |  7 ++++++-
 conans/test/integration/package_info_test.py   | 14 +++++++-------
 conans/test/unittests/model/build_info_test.py |  3 ++-
 3 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index f98f39980f7..54360a0be99 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -89,6 +89,7 @@ def libs(self):
 
     @libs.setter
     def libs(self, libs):
+        assert isinstance(libs, list), "'libs' attribute should be a list of strings"
         if self._components:
             raise ConanException("Setting first level libs is not supported when Components are "
                                  "already in use")
@@ -97,12 +98,13 @@ def libs(self, libs):
     @property
     def exes(self):
         if self._components:
-            return [component.exe for component in self._components.values() if component.exe]
+            return [component.exe for component in self._sorted_components if component.exe]
         else:
             return self._exes
 
     @exes.setter
     def exes(self, exes):
+        assert isinstance(exes, list), "'exes' attribute should be a list of strings"
         if self._components:
             raise ConanException("Setting first level exes is not supported when Components are "
                                  "already in use")
@@ -123,6 +125,7 @@ def system_deps(self):
 
     @system_deps.setter
     def system_deps(self, system_deps):
+        assert isinstance(system_deps, list), "'system_deps' attribute should be a list of strings"
         if self._components:
             raise ConanException("Setting first level system_deps is not supported when Components "
                                  "are already in use")
@@ -323,6 +326,7 @@ def lib(self):
 
     @lib.setter
     def lib(self, name):
+        assert isinstance(name, str), "'lib' attribute should be a string"
         if self._exe:
             raise ConanException("'.exe' is already set for this Component")
         self._lib = name
@@ -333,6 +337,7 @@ def exe(self):
 
     @exe.setter
     def exe(self, name):
+        assert isinstance(name, str), "'exe' attribute should be a string"
         if self._lib:
             raise ConanException("'.lib' is already set for this Component")
         self._exe = name
diff --git a/conans/test/integration/package_info_test.py b/conans/test/integration/package_info_test.py
index 98ea92998fb..df4fb7f40f3 100644
--- a/conans/test/integration/package_info_test.py
+++ b/conans/test/integration/package_info_test.py
@@ -170,9 +170,9 @@ class Consumer(ConanFile):
 
             def build(self):
                 # Global values
-                self.output.info("GLOBAL Include Paths: %s" % self.deps_cpp_info.include_paths)
-                self.output.info("GLOBAL Library Paths: %s" % self.deps_cpp_info.lib_paths)
-                self.output.info("GLOBAL Binary Paths: %s" % self.deps_cpp_info.bin_paths)
+                self.output.info("GLOBAL Include paths: %s" % self.deps_cpp_info.include_paths)
+                self.output.info("GLOBAL Library paths: %s" % self.deps_cpp_info.lib_paths)
+                self.output.info("GLOBAL Binary paths: %s" % self.deps_cpp_info.bin_paths)
                 self.output.info("GLOBAL Libs: %s" % self.deps_cpp_info.libs)
                 self.output.info("GLOBAL Exes: %s" % self.deps_cpp_info.exes)
                 # Deps values
@@ -217,13 +217,13 @@ def build(self):
         expected_global_binary_paths = [os.path.join(package_folder, "bin")]
         expected_global_libs = ["libstarlight", "ground", "libplanet", "solar", "magnetism", "libiss"]
         expected_global_exes = ["exelauncher"]
-        self.assertIn("GLOBAL Include Paths: %s" % expected_global_include_paths, client.out)
-        self.assertIn("GLOBAL Library Paths: %s" % expected_global_library_paths, client.out)
-        self.assertIn("GLOBAL Binary Paths: %s" % expected_global_binary_paths, client.out)
+        self.assertIn("GLOBAL Include paths: %s" % expected_global_include_paths, client.out)
+        self.assertIn("GLOBAL Library paths: %s" % expected_global_library_paths, client.out)
+        self.assertIn("GLOBAL Binary paths: %s" % expected_global_binary_paths, client.out)
         self.assertIn("GLOBAL Libs: %s" % expected_global_libs, client.out)
         self.assertIn("GLOBAL Exes: %s" % expected_global_exes, client.out)
 
-        self.assertIn("DEPS Include Paths: %s" % expected_global_include_paths, client.out)
+        self.assertIn("DEPS Include paths: %s" % expected_global_include_paths, client.out)
         self.assertIn("DEPS Library paths: %s" % expected_global_library_paths, client.out)
         self.assertIn("DEPS Binary paths: %s" % expected_global_binary_paths, client.out)
         self.assertIn("DEPS Libs: %s" % expected_global_libs, client.out)
diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index c0f6ea26e69..c89af8c1be8 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -184,7 +184,8 @@ def basic_components_test(self):
         self.assertEqual(component.lib, "libhola")
         with six.assertRaisesRegex(self, ConanException, "'.lib' is already set for this Component"):
             component.exe = "hola.exe"
-        component.lib = None
+
+        component = cpp_info["my_other_component"]
         component.exe = "hola.exe"
         self.assertEqual(component.lib, None)
         with six.assertRaisesRegex(self, ConanException, "'.exe' is already set for this Component"):

From b0852118b9d65e31b7e0848f59a990b16a66084e Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Thu, 20 Jun 2019 18:31:02 +0200
Subject: [PATCH 32/47] fix path repetition

---
 conans/model/build_info.py | 2 --
 1 file changed, 2 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index 54360a0be99..0c1febfab27 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -158,8 +158,6 @@ def _get_paths(self, path_name):
         if self._components:
             for dep_value in self._components.values():
                 abs_paths = self._filter_paths(getattr(dep_value, "%s_paths" % path_name))
-                if not getattr(self, "_%s_paths" % path_name):
-                    result.extend(abs_paths)
                 for path in abs_paths:
                     if path not in result:
                         result.append(path)

From ebd80e39939129ac45fcb69aeccee8a5e77c7b89 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Thu, 20 Jun 2019 18:31:23 +0200
Subject: [PATCH 33/47] remove endline

---
 conans/test/unittests/model/build_info_test.py | 1 -
 1 file changed, 1 deletion(-)

diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index c89af8c1be8..b88878994dc 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -364,4 +364,3 @@ def cppinfo_exes_test(self):
         with six.assertRaisesRegex(self, ConanException, "Setting first level exes is not supported "
                                                          "when Components are already in use"):
             info.exes = ["another_exe"]
-

From 63f8235e4682fe694953475345debe18f0d39a8a Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Thu, 20 Jun 2019 18:31:38 +0200
Subject: [PATCH 34/47] move default values dict

---
 conans/model/build_info.py | 48 ++++++++++++++++----------------------
 1 file changed, 20 insertions(+), 28 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index 0c1febfab27..106c8b6de86 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -222,17 +222,7 @@ class CppInfo(_CppInfo):
     def __init__(self, root_folder):
         super(CppInfo, self).__init__()
         self.rootpath = root_folder  # the full path of the package in which the conans is found
-        self.includedirs.append(DEFAULT_INCLUDE)
-        self.libdirs.append(DEFAULT_LIB)
-        self.bindirs.append(DEFAULT_BIN)
-        self.resdirs.append(DEFAULT_RES)
-        self.builddirs.append(DEFAULT_BUILD)
-        # public_deps is needed to accumulate list of deps for cmake targets
-        self.public_deps = []
-        self.configs = {}
-
-    def _check_dirs_values(self):
-        default_dirs_mapping = {
+        self._default_dirs_values = {
             "includedirs": [DEFAULT_INCLUDE],
             "libdirs": [DEFAULT_LIB],
             "bindirs": [DEFAULT_BIN],
@@ -240,23 +230,25 @@ def _check_dirs_values(self):
             "builddirs": [DEFAULT_BUILD],
             "srcdirs": []
         }
+        self.includedirs.extend(self._default_dirs_values["includedirs"])
+        self.libdirs.extend(self._default_dirs_values["libdirs"])
+        self.bindirs.extend(self._default_dirs_values["bindirs"])
+        self.resdirs.extend(self._default_dirs_values["resdirs"])
+        self.builddirs.extend(self._default_dirs_values["builddirs"])
+        # public_deps is needed to accumulate list of deps for cmake targets
+        self.public_deps = []
+        self.configs = {}
+
+    def _check_dirs_values(self):
         msg_template = "Using Components and global '{}' values ('{}') is not supported"
-        for dir_name in default_dirs_mapping:
+        for dir_name in self._default_dirs_values:
             dirs_value = getattr(self, dir_name)
-            if dirs_value is not None and dirs_value != default_dirs_mapping[dir_name]:
+            if dirs_value is not None and dirs_value != self._default_dirs_values[dir_name]:
                 raise ConanException(msg_template.format(dir_name, dirs_value))
 
     def _clear_dirs_values(self):
-        default_dirs_mapping = {
-            "includedirs": [DEFAULT_INCLUDE],
-            "libdirs": [DEFAULT_LIB],
-            "bindirs": [DEFAULT_BIN],
-            "resdirs": [DEFAULT_RES],
-            "builddirs": [DEFAULT_BUILD],
-            "srcdirs": []
-        }
-        for dir_name in default_dirs_mapping:
-            if getattr(self, dir_name) == default_dirs_mapping[dir_name]:
+        for dir_name in self._default_dirs_values:
+            if getattr(self, dir_name) == self._default_dirs_values[dir_name]:
                 self.__dict__[dir_name] = None
 
     def __getitem__(self, key):
@@ -278,11 +270,11 @@ def _get_cpp_info():
             result = _CppInfo()
             result.rootpath = self.rootpath
             result.sysroot = self.sysroot
-            result.includedirs.append(DEFAULT_INCLUDE)
-            result.libdirs.append(DEFAULT_LIB)
-            result.bindirs.append(DEFAULT_BIN)
-            result.resdirs.append(DEFAULT_RES)
-            result.builddirs.append(DEFAULT_BUILD)
+            result.includedirs.extend(self._default_dirs_values["includedirs"])
+            result.libdirs.extend(self._default_dirs_values["libdirs"])
+            result.bindirs.extend(self._default_dirs_values["bindirs"])
+            result.resdirs.extend(self._default_dirs_values["resdirs"])
+            result.builddirs.extend(self._default_dirs_values["builddirs"])
             return result
 
         return self.configs.setdefault(config, _get_cpp_info())

From 2c3e3d74d12b683a754f129cc69e0d5137a57d72 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Thu, 20 Jun 2019 18:34:24 +0200
Subject: [PATCH 35/47] Use is none

---
 conans/model/build_info.py | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index 106c8b6de86..ca47b8e10cb 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -167,37 +167,37 @@ def _get_paths(self, path_name):
 
     @property
     def include_paths(self):
-        if not self._include_paths:
+        if self._include_paths is None:
             self._include_paths = self._get_paths("include")
         return self._include_paths
 
     @property
     def lib_paths(self):
-        if not self._lib_paths:
+        if self._lib_paths is None:
             self._lib_paths = self._get_paths("lib")
         return self._lib_paths
 
     @property
     def src_paths(self):
-        if not self._src_paths:
+        if self._src_paths is None:
             self._src_paths = self._get_paths("src")
         return self._src_paths
 
     @property
     def bin_paths(self):
-        if not self._bin_paths:
+        if self._bin_paths is None:
             self._bin_paths = self._get_paths("bin")
         return self._bin_paths
 
     @property
     def build_paths(self):
-        if not self._build_paths:
+        if self._build_paths is None:
             self._build_paths = self._get_paths("build")
         return self._build_paths
 
     @property
     def res_paths(self):
-        if not self._res_paths:
+        if self._res_paths is None:
             self._res_paths = self._get_paths("res")
         return self._res_paths
 

From c01ba50510b10f30ecd80df7c5ba9844827eaf38 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Fri, 21 Jun 2019 10:00:05 +0200
Subject: [PATCH 36/47] Completed test

---
 conans/test/integration/package_info_test.py | 74 +++++++++++++++++---
 1 file changed, 65 insertions(+), 9 deletions(-)

diff --git a/conans/test/integration/package_info_test.py b/conans/test/integration/package_info_test.py
index df4fb7f40f3..2af8f3f7571 100644
--- a/conans/test/integration/package_info_test.py
+++ b/conans/test/integration/package_info_test.py
@@ -209,14 +209,44 @@ def build(self):
         client.run("create conanfile_consumer.py consumer/1.0@us/ch")
         package_folder = client.cache.package_layout(dep_ref).package(dep_pref)
 
-        expected_global_include_paths = [os.path.join(package_folder, "galaxy", "starlight"),
-                                         os.path.join(package_folder, "galaxy", "planet"),
-                                         os.path.join(package_folder, "galaxy", "iss")]
-        expected_global_library_paths = [os.path.join(package_folder, "lib"),
-                                         os.path.join(package_folder, "iss_libs")]
-        expected_global_binary_paths = [os.path.join(package_folder, "bin")]
-        expected_global_libs = ["libstarlight", "ground", "libplanet", "solar", "magnetism", "libiss"]
-        expected_global_exes = ["exelauncher"]
+        expected_comp_starlight_include_paths = [os.path.join(package_folder, "galaxy", "starlight")]
+        expected_comp_planet_include_paths = [os.path.join(package_folder, "galaxy", "planet")]
+        expected_comp_launcher_include_paths = []
+        expected_comp_iss_include_paths = [os.path.join(package_folder, "galaxy", "iss")]
+        expected_comp_starlight_library_paths = [os.path.join(package_folder, "lib")]
+        expected_comp_launcher_library_paths = [os.path.join(package_folder, "lib")]
+        expected_comp_planet_library_paths = [os.path.join(package_folder, "lib")]
+        expected_comp_iss_library_paths = [os.path.join(package_folder, "iss_libs")]
+        expected_comp_starlight_binary_paths = [os.path.join(package_folder, "bin")]
+        expected_comp_launcher_binary_paths = [os.path.join(package_folder, "bin")]
+        expected_comp_planet_binary_paths = [os.path.join(package_folder, "bin")]
+        expected_comp_iss_binary_paths = [os.path.join(package_folder, "bin")]
+        expected_comp_starlight_lib = "libstarlight"
+        expected_comp_planet_lib = "libplanet"
+        expected_comp_launcher_lib = None
+        expected_comp_iss_lib = "libiss"
+        expected_comp_starlight_system_deps = []
+        expected_comp_planet_system_deps = []
+        expected_comp_launcher_system_deps = ["ground"]
+        expected_comp_iss_system_deps = ["solar", "magnetism"]
+        expected_comp_starlight_exe = ""
+        expected_comp_planet_exe = ""
+        expected_comp_launcher_exe = "exelauncher"
+        expected_comp_iss_exe = ""
+
+        expected_global_include_paths = expected_comp_starlight_include_paths + \
+            expected_comp_planet_include_paths + expected_comp_iss_include_paths
+        expected_global_library_paths = expected_comp_starlight_library_paths + \
+            expected_comp_iss_library_paths
+        expected_global_binary_paths = expected_comp_starlight_binary_paths
+        expected_global_libs = expected_comp_starlight_system_deps
+        expected_global_libs.append(expected_comp_starlight_lib)
+        expected_global_libs.extend(expected_comp_launcher_system_deps)
+        expected_global_libs.append(expected_comp_planet_lib)
+        expected_global_libs.extend(expected_comp_iss_system_deps)
+        expected_global_libs.append(expected_comp_iss_lib)
+        expected_global_exes = [expected_comp_launcher_exe]
+
         self.assertIn("GLOBAL Include paths: %s" % expected_global_include_paths, client.out)
         self.assertIn("GLOBAL Library paths: %s" % expected_global_library_paths, client.out)
         self.assertIn("GLOBAL Binary paths: %s" % expected_global_binary_paths, client.out)
@@ -229,4 +259,30 @@ def build(self):
         self.assertIn("DEPS Libs: %s" % expected_global_libs, client.out)
         self.assertIn("DEPS Exes: %s" % expected_global_exes, client.out)
 
-        #TODO: Complete the test checking the output of the components
+        self.assertIn("COMP Starlight Include paths: %s" % expected_comp_starlight_include_paths,
+                      client.out)
+        self.assertIn("COMP Planet Include paths: %s" % expected_comp_planet_include_paths,
+                      client.out)
+        self.assertIn("COMP Launcher Include paths: %s" % expected_comp_launcher_include_paths,
+                      client.out)
+        self.assertIn("COMP ISS Include paths: %s" % expected_comp_iss_include_paths, client.out)
+        self.assertIn("COMP Starlight Library paths: %s" % expected_comp_starlight_library_paths,
+                      client.out)
+        self.assertIn("COMP Planet Library paths: %s" % expected_comp_planet_library_paths,
+                      client.out)
+        self.assertIn("COMP Launcher Library paths: %s" % expected_comp_launcher_library_paths,
+                      client.out)
+        self.assertIn("COMP ISS Library paths: %s" % expected_comp_iss_library_paths, client.out)
+        self.assertIn("COMP Starlight Binary paths: %s" % expected_comp_iss_binary_paths, client.out)
+        self.assertIn("COMP Planet Binary paths: %s" % expected_comp_planet_binary_paths, client.out)
+        self.assertIn("COMP Launcher Binary paths: %s" % expected_comp_launcher_binary_paths,
+                      client.out)
+        self.assertIn("COMP ISS Binary paths: %s" % expected_comp_iss_binary_paths, client.out)
+        self.assertIn("COMP Starlight Lib: %s" % expected_comp_starlight_lib, client.out)
+        self.assertIn("COMP Planet Lib: %s" % expected_comp_planet_lib, client.out)
+        self.assertIn("COMP Launcher Lib: %s" % expected_comp_launcher_lib, client.out)
+        self.assertIn("COMP ISS Lib: %s" % expected_comp_iss_lib, client.out)
+        self.assertIn("COMP Starlight Exe: %s" % expected_comp_starlight_exe, client.out)
+        self.assertIn("COMP Planet Exe: %s" % expected_comp_planet_exe, client.out)
+        self.assertIn("COMP Launcher Exe: %s" % expected_comp_launcher_exe, client.out)
+        self.assertIn("COMP ISS Exe: %s" % expected_comp_iss_exe, client.out)

From d15006048e9ec273d5a45f7423ac5096cd5c5a45 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Fri, 21 Jun 2019 10:11:29 +0200
Subject: [PATCH 37/47] Added deps_cpp_info.system_deps global

---
 conans/model/build_info.py                   |  1 +
 conans/test/integration/package_info_test.py | 15 +++++++++++++--
 2 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index ca47b8e10cb..02d5352fbde 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -383,6 +383,7 @@ def merge_lists(seq1, seq2):
         self.cflags = merge_lists(dep_cpp_info.cflags, self.cflags)
         self.sharedlinkflags = merge_lists(dep_cpp_info.sharedlinkflags, self.sharedlinkflags)
         self.exelinkflags = merge_lists(dep_cpp_info.exelinkflags, self.exelinkflags)
+        self.system_deps = merge_lists(dep_cpp_info.system_deps, self.system_deps)
 
         if not self.sysroot:
             self.sysroot = dep_cpp_info.sysroot
diff --git a/conans/test/integration/package_info_test.py b/conans/test/integration/package_info_test.py
index 2af8f3f7571..8a884df10ec 100644
--- a/conans/test/integration/package_info_test.py
+++ b/conans/test/integration/package_info_test.py
@@ -175,6 +175,7 @@ def build(self):
                 self.output.info("GLOBAL Binary paths: %s" % self.deps_cpp_info.bin_paths)
                 self.output.info("GLOBAL Libs: %s" % self.deps_cpp_info.libs)
                 self.output.info("GLOBAL Exes: %s" % self.deps_cpp_info.exes)
+                self.output.info("GLOBAL System deps: %s" % self.deps_cpp_info.system_deps)
                 # Deps values
                 for dep_key, dep_value in self.deps_cpp_info.dependencies:
                     self.output.info("DEPS Include paths: %s" % dep_value.include_paths)
@@ -182,6 +183,7 @@ def build(self):
                     self.output.info("DEPS Binary paths: %s" % dep_value.bin_paths)
                     self.output.info("DEPS Libs: %s" % dep_value.libs)
                     self.output.info("DEPS Exes: %s" % dep_value.exes)
+                    self.output.info("DEPS System deps: %s" % dep_value.system_deps)
                 # Components values
                 for dep_key, dep_value in self.deps_cpp_info.dependencies:
                     for comp_name, comp_value in dep_value.components.items():
@@ -192,6 +194,7 @@ def build(self):
                         self.output.info("COMP %s Lib: %s" % (comp_name, comp_value.lib))
                         self.output.info("COMP %s Exe: %s" % (comp_name, comp_value.exe))
                         self.output.info("COMP %s Deps: %s" % (comp_name, comp_value.deps))
+                        self.output.info("COMP %s System deps: %s" % (comp_name, comp_value.system_deps))
         """)
 
         client = TestClient()
@@ -239,25 +242,27 @@ def build(self):
         expected_global_library_paths = expected_comp_starlight_library_paths + \
             expected_comp_iss_library_paths
         expected_global_binary_paths = expected_comp_starlight_binary_paths
-        expected_global_libs = expected_comp_starlight_system_deps
-        expected_global_libs.append(expected_comp_starlight_lib)
+        expected_global_libs = expected_comp_starlight_system_deps + [expected_comp_starlight_lib]
         expected_global_libs.extend(expected_comp_launcher_system_deps)
         expected_global_libs.append(expected_comp_planet_lib)
         expected_global_libs.extend(expected_comp_iss_system_deps)
         expected_global_libs.append(expected_comp_iss_lib)
         expected_global_exes = [expected_comp_launcher_exe]
+        expected_global_system_deps = expected_comp_launcher_system_deps + expected_comp_iss_system_deps
 
         self.assertIn("GLOBAL Include paths: %s" % expected_global_include_paths, client.out)
         self.assertIn("GLOBAL Library paths: %s" % expected_global_library_paths, client.out)
         self.assertIn("GLOBAL Binary paths: %s" % expected_global_binary_paths, client.out)
         self.assertIn("GLOBAL Libs: %s" % expected_global_libs, client.out)
         self.assertIn("GLOBAL Exes: %s" % expected_global_exes, client.out)
+        self.assertIn("GLOBAL System deps: %s" % expected_global_system_deps, client.out)
 
         self.assertIn("DEPS Include paths: %s" % expected_global_include_paths, client.out)
         self.assertIn("DEPS Library paths: %s" % expected_global_library_paths, client.out)
         self.assertIn("DEPS Binary paths: %s" % expected_global_binary_paths, client.out)
         self.assertIn("DEPS Libs: %s" % expected_global_libs, client.out)
         self.assertIn("DEPS Exes: %s" % expected_global_exes, client.out)
+        self.assertIn("DEPS System deps: %s" % expected_global_system_deps, client.out)
 
         self.assertIn("COMP Starlight Include paths: %s" % expected_comp_starlight_include_paths,
                       client.out)
@@ -286,3 +291,9 @@ def build(self):
         self.assertIn("COMP Planet Exe: %s" % expected_comp_planet_exe, client.out)
         self.assertIn("COMP Launcher Exe: %s" % expected_comp_launcher_exe, client.out)
         self.assertIn("COMP ISS Exe: %s" % expected_comp_iss_exe, client.out)
+        self.assertIn("COMP Starlight System deps: %s" % expected_comp_starlight_system_deps,
+                      client.out)
+        self.assertIn("COMP Planet System deps: %s" % expected_comp_planet_system_deps, client.out)
+        self.assertIn("COMP Launcher System deps: %s" % expected_comp_launcher_system_deps,
+                      client.out)
+        self.assertIn("COMP ISS System deps: %s" % expected_comp_iss_system_deps, client.out)

From f61e2d99ec097bc525705c23b3458b3768b7a9e3 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Fri, 21 Jun 2019 11:40:44 +0200
Subject: [PATCH 38/47] simplified link order property, added test and fixed
 other ones

---
 conans/model/build_info.py                    | 28 ++++++++++---------
 .../test/unittests/model/build_info_test.py   | 17 ++++++++---
 2 files changed, 28 insertions(+), 17 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index 02d5352fbde..550d8234ea1 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -55,22 +55,24 @@ def _sorted_components(self):
         Sorted components from less dependent to the most one
         :return: ordered list of components
         """
-        # Sort first elements with less items in .deps attribute
-        comps = sorted(self._components.values(), key=lambda component: len(component.deps))
         # Save name of unsorted elements
-        unsorted_names = [comp.name for comp in comps]
-
+        unsorted_names = self._components.keys()
         sorted_comps = []
+        element = unsorted_names[0]
         while unsorted_names:
-            for comp in comps:
-                # If element is already sorted, continue
-                if comp.name not in unsorted_names:
-                    continue
-                # If element does not have deps or all of its deps are already sorted, sort this
-                # element and remove it from the unsorted list
-                elif not comp.deps or not [dep for dep in comp.deps if dep in unsorted_names]:
-                    sorted_comps.append(comp)
-                    unsorted_names.remove(comp.name)
+            try:
+                deps = self._components[element].deps
+            except KeyError as e:
+                raise ConanException("Component %s not found in cpp_info object" % e)
+            if all(dep in [c.name for c in sorted_comps] for dep in deps):
+                sorted_comps.append(self._components[element])
+                unsorted_names.remove(element)
+                element = unsorted_names[0] if unsorted_names else None
+            else:
+                for dep in deps:
+                    if dep not in [c.name for c in sorted_comps]:
+                        element = dep
+                        break
         return sorted_comps
 
     @property
diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index b88878994dc..3fa5f43a377 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -221,7 +221,7 @@ def cpp_info_system_deps_test(self):
         info["LIB2"].system_deps = ["sys2"]
         info["LIB1"].deps = ["LIB3"]
         info["LIB3"].system_deps = ["sys3", "sys2"]
-        self.assertEqual(['sys2', 'sys3', 'sys1', 'sys11'], info.libs)
+        self.assertEqual(['sys3', 'sys2', 'sys1', 'sys11'], info.libs)
         info["LIB3"].system_deps = [None, "sys2"]
         self.assertEqual(['sys2', 'sys1', 'sys11'], info.libs)
 
@@ -241,15 +241,16 @@ def cpp_info_libs_system_deps_order_test(self):
         info["LIB1"].deps = ["LIB2"]
         info["LIB2"].lib = "lib2"
         info["LIB2"].system_deps = ["sys2"]
-        info["LIB1"].deps = ["LIB3"]
+        info["LIB2"].deps = ["LIB3"]
         info["LIB3"].lib = "lib3"
         info["LIB3"].system_deps = ["sys3", "sys2"]
-        self.assertEqual(['sys2', 'lib2', 'sys3', 'lib3', 'sys1', 'sys11', 'lib1'], info.libs)
-        self.assertEqual(['sys2', 'sys3', 'sys1', 'sys11'], info.system_deps)
+        self.assertEqual(['sys3', 'sys2', 'lib3', 'lib2', 'sys1', 'sys11', 'lib1'], info.libs)
+        self.assertEqual(['sys3', 'sys2', 'sys1', 'sys11'], info.system_deps)
 
     def cpp_info_link_order_test(self):
 
         def _assert_link_order(sorted_libs):
+            assert sorted_libs, "'sorted_libs' is empty"
             for num, lib in enumerate(sorted_libs):
                 component_name = lib[-1]
                 for dep in info[component_name].deps:
@@ -297,6 +298,14 @@ def _assert_link_order(sorted_libs):
         info["B"].deps = []
         _assert_link_order(info.libs)
 
+    def cppinfo_inexistent_component_dep_test(self):
+        info = CppInfo(None)
+        info["LIB1"].lib = "lib1"
+        info["LIB1"].deps = ["LIB2"]
+        with six.assertRaisesRegex(self, ConanException, "Component 'LIB2' not found in cpp_info "
+                                                         "object"):
+            info.libs
+
     def cppinfo_dirs_test(self):
         folder = temp_folder()
         info = CppInfo(folder)

From 0dbb94303389aa49edeb954f2d2caf47df09605a Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Fri, 21 Jun 2019 11:56:04 +0200
Subject: [PATCH 39/47] get paths in order too

---
 conans/model/build_info.py                   | 4 ++--
 conans/test/integration/package_info_test.py | 3 ++-
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index 550d8234ea1..d958f13aa1a 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -56,7 +56,7 @@ def _sorted_components(self):
         :return: ordered list of components
         """
         # Save name of unsorted elements
-        unsorted_names = self._components.keys()
+        unsorted_names = list(self._components.keys())
         sorted_comps = []
         element = unsorted_names[0]
         while unsorted_names:
@@ -158,7 +158,7 @@ def _get_paths(self, path_name):
         result = []
 
         if self._components:
-            for dep_value in self._components.values():
+            for dep_value in self._sorted_components:
                 abs_paths = self._filter_paths(getattr(dep_value, "%s_paths" % path_name))
                 for path in abs_paths:
                     if path not in result:
diff --git a/conans/test/integration/package_info_test.py b/conans/test/integration/package_info_test.py
index 8a884df10ec..d5aec0e09b9 100644
--- a/conans/test/integration/package_info_test.py
+++ b/conans/test/integration/package_info_test.py
@@ -243,8 +243,9 @@ def build(self):
             expected_comp_iss_library_paths
         expected_global_binary_paths = expected_comp_starlight_binary_paths
         expected_global_libs = expected_comp_starlight_system_deps + [expected_comp_starlight_lib]
-        expected_global_libs.extend(expected_comp_launcher_system_deps)
+        expected_global_libs.extend(expected_comp_planet_system_deps)
         expected_global_libs.append(expected_comp_planet_lib)
+        expected_global_libs.extend(expected_comp_launcher_system_deps)
         expected_global_libs.extend(expected_comp_iss_system_deps)
         expected_global_libs.append(expected_comp_iss_lib)
         expected_global_exes = [expected_comp_launcher_exe]

From c3da77016481679f13d223a99d8d38dc92615657 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Fri, 21 Jun 2019 11:59:29 +0200
Subject: [PATCH 40/47] docstring

---
 conans/model/build_info.py | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index d958f13aa1a..5e67d680aae 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -134,6 +134,9 @@ def system_deps(self, system_deps):
         self._system_deps = system_deps
 
     def __getitem__(self, key):
+        """
+        This is called when the user accesses to a component: self.cpp_info["whatever"]
+        """
         if self._libs:
             raise ConanException("Usage of Components with '.libs' values is not allowed")
         if key not in self._components.keys():

From 195665cbc6a499016d982d68b47c80a001b1b728 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Fri, 21 Jun 2019 12:06:24 +0200
Subject: [PATCH 41/47] improve system_deps test

---
 conans/test/unittests/model/build_info_test.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index 3fa5f43a377..5a5dc66cd15 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -219,17 +219,18 @@ def cpp_info_system_deps_test(self):
         info["LIB1"].system_deps = ["sys1", "sys11"]
         info["LIB1"].deps = ["LIB2"]
         info["LIB2"].system_deps = ["sys2"]
-        info["LIB1"].deps = ["LIB3"]
+        info["LIB2"].deps = ["LIB3"]
         info["LIB3"].system_deps = ["sys3", "sys2"]
         self.assertEqual(['sys3', 'sys2', 'sys1', 'sys11'], info.libs)
+        self.assertEqual(['sys3', 'sys2', 'sys1', 'sys11'], info.system_deps)
         info["LIB3"].system_deps = [None, "sys2"]
         self.assertEqual(['sys2', 'sys1', 'sys11'], info.libs)
+        self.assertEqual(['sys2', 'sys1', 'sys11'], info.system_deps)
 
         with six.assertRaisesRegex(self, ConanException, "Setting first level system_deps is not "
                                                          "supported when Components are already in "
                                                          "use"):
             info.system_deps = ["random_system"]
-        self.assertEqual(['sys2', 'sys1', 'sys11'], info.system_deps)
 
     def cpp_info_libs_system_deps_order_test(self):
         """

From ff6fca347443be6f1216100833c3d0655c6980f1 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Fri, 21 Jun 2019 13:15:30 +0200
Subject: [PATCH 42/47] merge methods

---
 conans/model/build_info.py | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index 5e67d680aae..e0b7fbcaea0 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -244,23 +244,19 @@ def __init__(self, root_folder):
         self.public_deps = []
         self.configs = {}
 
-    def _check_dirs_values(self):
-        msg_template = "Using Components and global '{}' values ('{}') is not supported"
+    def _check_and_clear_dirs_values(self):
         for dir_name in self._default_dirs_values:
             dirs_value = getattr(self, dir_name)
             if dirs_value is not None and dirs_value != self._default_dirs_values[dir_name]:
+                msg_template = "Using Components and global '{}' values ('{}') is not supported"
                 raise ConanException(msg_template.format(dir_name, dirs_value))
-
-    def _clear_dirs_values(self):
-        for dir_name in self._default_dirs_values:
-            if getattr(self, dir_name) == self._default_dirs_values[dir_name]:
+            else:
                 self.__dict__[dir_name] = None
 
     def __getitem__(self, key):
         if self._libs or self._exes:
             raise ConanException("Usage of Components with '.libs' or '.exes' values is not allowed")
-        self._clear_dirs_values()
-        self._check_dirs_values()
+        self._check_and_clear_dirs_values()
         if key not in self._components:
             self._components[key] = Component(key, self.rootpath)
         return self._components[key]

From 1c95d9b773e065b27a74b527cd88e2936c431031 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Fri, 21 Jun 2019 17:13:57 +0200
Subject: [PATCH 43/47] Added components to the cpp_info json output

---
 conans/client/recorder/action_recorder.py     | 17 +++++++--
 conans/model/build_info.py                    | 10 +++++
 .../functional/command/json_output_test.py    | 37 +++++++++++++++++++
 3 files changed, 60 insertions(+), 4 deletions(-)

diff --git a/conans/client/recorder/action_recorder.py b/conans/client/recorder/action_recorder.py
index 9efa287a76a..0be7610b0e9 100644
--- a/conans/client/recorder/action_recorder.py
+++ b/conans/client/recorder/action_recorder.py
@@ -24,18 +24,27 @@
 
 def _cpp_info_to_dict(cpp_info):
     doc = {}
-    for it, value in vars(cpp_info).items():
-        if (it.startswith("_") and it != "_libs") or not value:
+    items = vars(cpp_info).items()
+    if not cpp_info._components:
+        items = items + [("libs", cpp_info.libs), ("exes", cpp_info.exes),
+                         ("system_deps", cpp_info.system_deps)]
+    for key, value in items:
+        if key.startswith("_components") and value:
+            doc["components"] = {}
+            for comp_key, comp_value in value.items():
+                doc["components"][comp_key] = comp_value.as_dict()
+                continue
+
+        if key.startswith("_") or not value:
             continue
 
-        if it == "configs":
+        if key == "configs":
             configs_data = {}
             for cfg_name, cfg_cpp_info in value.items():
                 configs_data[cfg_name] = _cpp_info_to_dict(cfg_cpp_info)
             doc["configs"] = configs_data
             continue
 
-        key = "libs" if it == "_libs" else it
         doc[key] = value
     return doc
 
diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index e0b7fbcaea0..ec949b52965 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -304,6 +304,16 @@ def __init__(self, name, root_folder):
         self.exelinkflags = []
         self._filter_empty = True
 
+    def as_dict(self):
+        result = {}
+        for key, value in vars(self).items():
+            if key.startswith("_"):
+                continue
+            result[key] = value
+        result["lib"] = self.lib
+        result["exe"] = self.exe
+        return result
+
     def _filter_paths(self, paths):
         abs_paths = [os.path.join(self._rootpath, p) for p in paths]
         if self._filter_empty:
diff --git a/conans/test/functional/command/json_output_test.py b/conans/test/functional/command/json_output_test.py
index ef6c72d5c0c..b9a84271112 100644
--- a/conans/test/functional/command/json_output_test.py
+++ b/conans/test/functional/command/json_output_test.py
@@ -218,3 +218,40 @@ def package_info(self):
         for dupe in dupe_nodes:
             self.assertEqual(cpp_info[dupe], cpp_info_debug[dupe])
             self.assertEqual(cpp_info[dupe], cpp_info_release[dupe])
+
+    def test_json_create_cpp_info_components(self):
+        conanfile = textwrap.dedent("""
+            from conans import ConanFile
+
+            class Lib(ConanFile):
+                def package_info(self):
+                    self.cpp_info.name = "Boost"
+                    self.cpp_info["whatever"].lib = "libwhaterver"
+                    self.cpp_info["whatever"].system_deps = ["sysdep1", "sysdep2"]
+                    self.cpp_info["whenever"].lib = "libwhenever"
+                    self.cpp_info["whenever"].deps = ["whatever"]
+                    self.cpp_info["however"].exe = "exehowever"
+                    self.cpp_info["however"].system_deps = ["pthread"]
+                    self.cpp_info["however"].deps = ["whenever"]
+            """)
+        self.client.save({'conanfile.py': conanfile})
+        self.client.run("create . name/version@user/channel --json=myfile.json")
+        my_json = load(os.path.join(self.client.current_folder, "myfile.json"))
+        my_json = json.loads(my_json)
+
+        # Nodes with cpp_info
+        cpp_info = my_json["installed"][0]["packages"][0]["cpp_info"]
+
+        self.assertEqual("Boost", cpp_info["name"])
+        self.assertFalse("includedirs" in cpp_info)
+        self.assertFalse("includedirs" in cpp_info)
+        self.assertFalse("includedirs" in cpp_info)
+        self.assertFalse("includedirs" in cpp_info)
+        self.assertFalse("libs" in cpp_info)
+        self.assertFalse("exes" in cpp_info)
+        self.assertFalse("system_deps" in cpp_info)
+        self.assertEqual("libwhaterver", cpp_info["components"]["whatever"]["lib"])
+        self.assertEqual("libwhenever", cpp_info["components"]["whenever"]["lib"])
+        self.assertEqual("exehowever", cpp_info["components"]["however"]["exe"])
+        self.assertEqual(["pthread"], cpp_info["components"]["however"]["system_deps"])
+        self.assertEqual(["sysdep1", "sysdep2"], cpp_info["components"]["whatever"]["system_deps"])

From 6cdda0f604f917a4ac09e85071ebc0cd9643a4b0 Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Mon, 24 Jun 2019 19:18:24 +0200
Subject: [PATCH 44/47] loop check with test

---
 conans/model/build_info.py                    | 10 +++++++
 .../test/unittests/model/build_info_test.py   | 26 +++++++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index ec949b52965..74130b4a947 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -59,6 +59,7 @@ def _sorted_components(self):
         unsorted_names = list(self._components.keys())
         sorted_comps = []
         element = unsorted_names[0]
+        search_buffer = []
         while unsorted_names:
             try:
                 deps = self._components[element].deps
@@ -67,10 +68,19 @@ def _sorted_components(self):
             if all(dep in [c.name for c in sorted_comps] for dep in deps):
                 sorted_comps.append(self._components[element])
                 unsorted_names.remove(element)
+                search_buffer = []
                 element = unsorted_names[0] if unsorted_names else None
             else:
                 for dep in deps:
                     if dep not in [c.name for c in sorted_comps]:
+                        if dep in search_buffer:
+                            if len(search_buffer) > 1:
+                                search_buffer.remove(dep)
+                            raise ConanException("Detected loop calculating the link order of "
+                                                 "components. Please check the '.deps' and resolve "
+                                                 "the circular depency of '%s' with %s" %
+                                                 (dep, search_buffer))
+                        search_buffer.append(dep)
                         element = dep
                         break
         return sorted_comps
diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index 5a5dc66cd15..8e97d8e858d 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -307,6 +307,32 @@ def cppinfo_inexistent_component_dep_test(self):
                                                          "object"):
             info.libs
 
+    def cpp_info_components_dep_loop_test(self):
+        info = CppInfo(None)
+        info["LIB1"].lib = "lib1"
+        info["LIB1"].deps = ["LIB1"]
+        msg_template = "Detected loop calculating the link order of components. Please check the " \
+                       "'.deps' and resolve the circular depency of '{}' with {}"
+        with six.assertRaisesRegex(self, ConanException, msg_template.format("LIB1", ["LIB1"])):
+            info.libs
+        info = CppInfo(None)
+        info["LIB1"].lib = "lib1"
+        info["LIB1"].deps = ["LIB2"]
+        info["LIB2"].lib = "lib2"
+        info["LIB2"].deps = ["LIB1", "LIB2"]
+        with six.assertRaisesRegex(self, ConanException, msg_template.format("LIB2", ["LIB1"])):
+            info.libs
+        info = CppInfo(None)
+        info["LIB1"].lib = "lib1"
+        info["LIB1"].deps = ["LIB2"]
+        info["LIB2"].lib = "lib2"
+        info["LIB2"].deps = ["LIB3"]
+        info["LIB3"].lib = "lib3"
+        info["LIB3"].deps = ["LIB1"]
+        with six.assertRaisesRegex(self, ConanException,
+                                   msg_template.format("LIB3", ["LIB1", "LIB2"])):
+            info.libs
+
     def cppinfo_dirs_test(self):
         folder = temp_folder()
         info = CppInfo(folder)

From 93f6c8d931cd54cc6aa5d0e9df498209cdf6f200 Mon Sep 17 00:00:00 2001
From: Luis Martinez de Bartolome <lasote@gmail.com>
Date: Tue, 25 Jun 2019 09:44:28 +0200
Subject: [PATCH 45/47] 5090 fixes

---
 conans/client/recorder/action_recorder.py     |  2 +-
 conans/model/build_info.py                    | 48 +++++++------------
 conans/test/integration/package_info_test.py  |  3 --
 .../test/unittests/model/build_info_test.py   | 14 +++---
 4 files changed, 23 insertions(+), 44 deletions(-)

diff --git a/conans/client/recorder/action_recorder.py b/conans/client/recorder/action_recorder.py
index 0be7610b0e9..f612d6bc843 100644
--- a/conans/client/recorder/action_recorder.py
+++ b/conans/client/recorder/action_recorder.py
@@ -24,7 +24,7 @@
 
 def _cpp_info_to_dict(cpp_info):
     doc = {}
-    items = vars(cpp_info).items()
+    items = list(vars(cpp_info).items())
     if not cpp_info._components:
         items = items + [("libs", cpp_info.libs), ("exes", cpp_info.exes),
                          ("system_deps", cpp_info.system_deps)]
diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index 74130b4a947..430520b0357 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -51,39 +51,23 @@ def __init__(self):
 
     @property
     def _sorted_components(self):
-        """
-        Sorted components from less dependent to the most one
-        :return: ordered list of components
-        """
-        # Save name of unsorted elements
-        unsorted_names = list(self._components.keys())
-        sorted_comps = []
-        element = unsorted_names[0]
-        search_buffer = []
-        while unsorted_names:
-            try:
-                deps = self._components[element].deps
-            except KeyError as e:
-                raise ConanException("Component %s not found in cpp_info object" % e)
-            if all(dep in [c.name for c in sorted_comps] for dep in deps):
-                sorted_comps.append(self._components[element])
-                unsorted_names.remove(element)
-                search_buffer = []
-                element = unsorted_names[0] if unsorted_names else None
+        ordered = OrderedDict()
+        while len(ordered) != len(self._components):
+            # Search for next element to be processed
+            for comp_name, comp in self._components.items():
+                if comp_name in ordered:
+                    continue
+                # check if all the deps are declared
+                if not all([dep in self._components for dep in comp.deps]):
+                    raise ConanException("Component '%s' declares a missing dependency" % comp.name)
+                # check if all the deps are already added to ordered
+                if all([dep in ordered for dep in comp.deps]):
+                    break
             else:
-                for dep in deps:
-                    if dep not in [c.name for c in sorted_comps]:
-                        if dep in search_buffer:
-                            if len(search_buffer) > 1:
-                                search_buffer.remove(dep)
-                            raise ConanException("Detected loop calculating the link order of "
-                                                 "components. Please check the '.deps' and resolve "
-                                                 "the circular depency of '%s' with %s" %
-                                                 (dep, search_buffer))
-                        search_buffer.append(dep)
-                        element = dep
-                        break
-        return sorted_comps
+                raise ConanException("There is a loop between your cpp_info declared components")
+
+            ordered[comp_name] = comp
+        return ordered.values()
 
     @property
     def libs(self):
diff --git a/conans/test/integration/package_info_test.py b/conans/test/integration/package_info_test.py
index d5aec0e09b9..da5a6959116 100644
--- a/conans/test/integration/package_info_test.py
+++ b/conans/test/integration/package_info_test.py
@@ -2,9 +2,6 @@
 import textwrap
 import unittest
 
-import six
-
-from conans.errors import ConanException
 from conans.model.ref import ConanFileReference, PackageReference
 from conans.paths import CONANFILE, CONANFILE_TXT
 from conans.test.utils.tools import TestClient, NO_SETTINGS_PACKAGE_ID
diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index 8e97d8e858d..cb79f3a377e 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -303,24 +303,23 @@ def cppinfo_inexistent_component_dep_test(self):
         info = CppInfo(None)
         info["LIB1"].lib = "lib1"
         info["LIB1"].deps = ["LIB2"]
-        with six.assertRaisesRegex(self, ConanException, "Component 'LIB2' not found in cpp_info "
-                                                         "object"):
+        with six.assertRaisesRegex(self, ConanException, "Component 'LIB1' "
+                                                         "declares a missing dependency"):
             info.libs
 
     def cpp_info_components_dep_loop_test(self):
         info = CppInfo(None)
         info["LIB1"].lib = "lib1"
         info["LIB1"].deps = ["LIB1"]
-        msg_template = "Detected loop calculating the link order of components. Please check the " \
-                       "'.deps' and resolve the circular depency of '{}' with {}"
-        with six.assertRaisesRegex(self, ConanException, msg_template.format("LIB1", ["LIB1"])):
+        msg = "There is a loop between your cpp_info declared components"
+        with six.assertRaisesRegex(self, ConanException, msg):
             info.libs
         info = CppInfo(None)
         info["LIB1"].lib = "lib1"
         info["LIB1"].deps = ["LIB2"]
         info["LIB2"].lib = "lib2"
         info["LIB2"].deps = ["LIB1", "LIB2"]
-        with six.assertRaisesRegex(self, ConanException, msg_template.format("LIB2", ["LIB1"])):
+        with six.assertRaisesRegex(self, ConanException, msg):
             info.libs
         info = CppInfo(None)
         info["LIB1"].lib = "lib1"
@@ -329,8 +328,7 @@ def cpp_info_components_dep_loop_test(self):
         info["LIB2"].deps = ["LIB3"]
         info["LIB3"].lib = "lib3"
         info["LIB3"].deps = ["LIB1"]
-        with six.assertRaisesRegex(self, ConanException,
-                                   msg_template.format("LIB3", ["LIB1", "LIB2"])):
+        with six.assertRaisesRegex(self, ConanException, msg):
             info.libs
 
     def cppinfo_dirs_test(self):

From 1036bd8467dca05372a1d63bb75217680f3638ac Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Tue, 25 Jun 2019 10:08:40 +0200
Subject: [PATCH 46/47] improved message

---
 conans/model/build_info.py                     | 3 ++-
 conans/test/unittests/model/build_info_test.py | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index 430520b0357..60a43566abf 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -64,7 +64,8 @@ def _sorted_components(self):
                 if all([dep in ordered for dep in comp.deps]):
                     break
             else:
-                raise ConanException("There is a loop between your cpp_info declared components")
+                raise ConanException("There is a dependency loop in the components declared in "
+                                     "'self.cpp_info'")
 
             ordered[comp_name] = comp
         return ordered.values()
diff --git a/conans/test/unittests/model/build_info_test.py b/conans/test/unittests/model/build_info_test.py
index cb79f3a377e..bfe26c21380 100644
--- a/conans/test/unittests/model/build_info_test.py
+++ b/conans/test/unittests/model/build_info_test.py
@@ -311,7 +311,7 @@ def cpp_info_components_dep_loop_test(self):
         info = CppInfo(None)
         info["LIB1"].lib = "lib1"
         info["LIB1"].deps = ["LIB1"]
-        msg = "There is a loop between your cpp_info declared components"
+        msg = "There is a dependency loop in the components declared in 'self.cpp_info'"
         with six.assertRaisesRegex(self, ConanException, msg):
             info.libs
         info = CppInfo(None)

From 9696a4a8f4f29360059093904c1c522a0a4a943d Mon Sep 17 00:00:00 2001
From: danimtb <danimanzaneque@gmail.com>
Date: Tue, 25 Jun 2019 12:30:46 +0200
Subject: [PATCH 47/47] remove cppflags from component class

---
 conans/model/build_info.py | 1 -
 1 file changed, 1 deletion(-)

diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index 60a43566abf..0b22d271ebc 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -293,7 +293,6 @@ def __init__(self, name, root_folder):
         self.srcdirs = []
         self.defines = []
         self.cflags = []
-        self.cppflags = []
         self.cxxflags = []
         self.sharedlinkflags = []
         self.exelinkflags = []