Repository: conan-io/conan

Issue:
[question] How to specify 'includedirs'?
This refers to Windows and Visual Studio. 
I try to change default includedirs of a components on Windows, in package_info() method

I say
self.cpp_info.components["BASE"].includedirs = ['include/Release', 'include']
and get in CONAN_INCLUDE_DIRS: ['include', 'include/Release'] - reverse order!
in conanbuildinfo.cmake, I mean

I say
self.cpp_info.components["BASE"].includedirs = ['include', 'include/Release']
and get in CONAN_INCLUDE_DIRS: ['include', 'include/Release'] - correct, but not what I want

What I really want is
self.cpp_info.components["BASE"].includedirs = ['include/$(Configuration)', 'include']
but in this case I get CONAN_INCLUDE_DIRS: ['include'] - which is totally wrong








Discussion:
Hi @gouriano,

What generator are you using to check the result of CONAN_INCLUDE_DIRS?
It is CMake
I am looking at the contents of conanbuildinfo.cmake,

So not sure what might be happening here but I guess is something related to repeated directories across the dependency graph. CONAN_INCLUDE_DIRS variable aggregates the include directories of all the dependencies in the graph you are installing. As "include" is the value by default, it might be first in the list because it is coming from an upstream dependency.

Another option could be that the generator is picking the value from ``cpp_info.includedirs`` and ``cpp_info.components["BASE"].includedirs``, but I think doing this is not allowed:

```
self.cpp_info.includedirs = []
self.cpp_info.components["BASE"].includedirs = ['include/Release', 'include']
```

Could you give it a go and let me know? thank you
What you suggest cannot work, because:
ConanException: CxxTest/0.1.6 package_info(): self.cpp_info.components cannot be used with self.cpp_info global values at the same time
(Yes, I have verified it)

I have tried few more variants:
When I specify, for example (note that all these directories exist in my package root)
self.cpp_info.components["BASE"].includedirs = ['cmake', 'include/common', 'include']
I get in CONAN_INCLUDE_DIRS = ['include', 'cmake', 'include/common']
'include' always goes first.

When I specify a directory which does not exits in package root:
self.cpp_info.components["BASE"].includedirs = ['notfound', 'include']
it is simply dropped. 
That is not good, I believe. Nonexistent directory is my problem (I write recipe), not yours.
I mean I still want to be able to specify
self.cpp_info.components["BASE"].includedirs = ['include/$(Configuration)', 'include']

Thanks


I have figured out how you can reproduce it.
Create new Conan package:
`conan new hello/0.1`
Change package_info into the following

    def package_info(self):
        self.cpp_info.libs = ["hello"]
        self.cpp_info.includedirs = ['lib', 'include']

run "conan create ."
Then, in a separate directory, create conanfile.txt:

    [requires]
    hello/0.1
    [generators]
    cmake

run "conan install ."
In the generated conanbuildinfo.cmake, CONAN_INCLUDE_DIRS in the correct order: ['lib', 'include']

Now change package_info

    def package_info(self):
        self.cpp_info.components["BASE"].defines.append("_UNICODE")
        self.cpp_info.components["BASE"].includedirs = ['lib', 'include']
        self.cpp_info.components["hellolib"].libs = ["hello"]
        self.cpp_info.components["hellolib"].requires = ["BASE"]

again, "conan create .", then "conan install .", then look into conanbuildinfo.cmake.
CONAN_INCLUDE_DIRS are now in wrong order: [include', 'lib']

So, is it a bug? or I do something wrong?
I use Conan v1.35.2



Resolving patch:
diff --git a/conans/model/build_info.py b/conans/model/build_info.py
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -556,14 +556,29 @@ def __getattr__(self, item):
             attr = self._cpp_info.__getattr__(item)
         return attr
 
-    def _aggregated_values(self, item, agg_func=merge_lists):
+    def _aggregated_dict_values(self, item):
         values = getattr(self, "_%s" % item)
         if values is not None:
             return values
-        values = getattr(self._cpp_info, item)
         if self._cpp_info.components:
+            values = {}
             for component in self._get_sorted_components().values():
-                values = agg_func(values, getattr(component, item))
+                values = merge_dicts(values, getattr(component, item))
+        else:
+            values = getattr(self._cpp_info, item)
+        setattr(self, "_%s" % item, values)
+        return values
+
+    def _aggregated_list_values(self, item):
+        values = getattr(self, "_%s" % item)
+        if values is not None:
+            return values
+        if self._cpp_info.components:
+            values = []
+            for component in self._get_sorted_components().values():
+                values = merge_lists(values, getattr(component, item))
+        else:
+            values = getattr(self._cpp_info, item)
         setattr(self, "_%s" % item, values)
         return values
 
@@ -614,71 +629,71 @@ def _get_sorted_components(self):
 
     @property
     def build_modules_paths(self):
-        return self._aggregated_values("build_modules_paths", agg_func=merge_dicts)
+        return self._aggregated_dict_values("build_modules_paths")
 
     @property
     def include_paths(self):
-        return self._aggregated_values("include_paths")
+        return self._aggregated_list_values("include_paths")
 
     @property
     def lib_paths(self):
-        return self._aggregated_values("lib_paths")
+        return self._aggregated_list_values("lib_paths")
 
     @property
     def src_paths(self):
-        return self._aggregated_values("src_paths")
+        return self._aggregated_list_values("src_paths")
 
     @property
     def bin_paths(self):
-        return self._aggregated_values("bin_paths")
+        return self._aggregated_list_values("bin_paths")
 
     @property
     def build_paths(self):
-        return self._aggregated_values("build_paths")
+        return self._aggregated_list_values("build_paths")
 
     @property
     def res_paths(self):
-        return self._aggregated_values("res_paths")
+        return self._aggregated_list_values("res_paths")
 
     @property
     def framework_paths(self):
-        return self._aggregated_values("framework_paths")
+        return self._aggregated_list_values("framework_paths")
 
     @property
     def libs(self):
-        return self._aggregated_values("libs")
+        return self._aggregated_list_values("libs")
 
     @property
     def system_libs(self):
-        return self._aggregated_values("system_libs")
+        return self._aggregated_list_values("system_libs")
 
     @property
     def frameworks(self):
-        return self._aggregated_values("frameworks")
+        return self._aggregated_list_values("frameworks")
 
     @property
     def defines(self):
-        return self._aggregated_values("defines")
+        return self._aggregated_list_values("defines")
 
     @property
     def cxxflags(self):
-        return self._aggregated_values("cxxflags")
+        return self._aggregated_list_values("cxxflags")
 
     @property
     def cflags(self):
-        return self._aggregated_values("cflags")
+        return self._aggregated_list_values("cflags")
 
     @property
     def sharedlinkflags(self):
-        return self._aggregated_values("sharedlinkflags")
+        return self._aggregated_list_values("sharedlinkflags")
 
     @property
     def exelinkflags(self):
-        return self._aggregated_values("exelinkflags")
+        return self._aggregated_list_values("exelinkflags")
 
     @property
     def requires(self):
-        return self._aggregated_values("requires")
+        return self._aggregated_list_values("requires")
 
 
 class DepsCppInfo(_BaseDepsCppInfo):


Test patch:
diff --git a/conans/test/unittests/model/build_info/components_test.py b/conans/test/unittests/model/build_info/components_test.py
--- a/conans/test/unittests/model/build_info/components_test.py
+++ b/conans/test/unittests/model/build_info/components_test.py
@@ -414,3 +414,14 @@ def test_deps_cpp_info_components(self):
         self.assertListEqual([os.path.join(folder, "include")], list(deps_cpp_info.include_paths))
         self.assertListEqual([os.path.join(folder, "include")],
                              list(deps_cpp_info["my_lib"].components["Component"].include_paths))
+
+    def test_deps_cpp_info_components_includedirs(self):
+        info = CppInfo("my_lib", "root")
+        info.components["component"].includedirs = ["include1", "include2"]
+        info.components["component"].filter_empty = False
+        dep_info = DepCppInfo(info)
+        expected = [os.path.join("root", "include1"), os.path.join("root", "include2")]
+        self.assertListEqual(expected, list(dep_info.include_paths))
+        deps_cpp_info = DepsCppInfo()
+        deps_cpp_info.add("my_lib", dep_info)
+        self.assertListEqual(expected, list(deps_cpp_info.includedirs))