Repository: conan-io/conan

Issue:
build_requirements is ignored
I have A package, which build_requires B package. And C package requires A, build_requires B. When I execute "conan install" for C, conan will skip B. If I remove requires A, conan will not skip B. What I want is conan will install A and B. Any help you can provide would be great.

Thanks

To help us debug your issue please explain:

To help us debug your issue please explain:

- [x] I've read the [CONTRIBUTING guide](https://github.com/conan-io/conan/blob/develop/.github/CONTRIBUTING.md).
- [x] I've specified the Conan version, operating system version and any tool that can be relevant.
- [x] I've explained the steps to reproduce the error or the motivation/use case of the question/suggestion.




Discussion:
Hi @xyz1001 

I am trying to reproduce your case, but so far no success. Please check the following test, that is passing:

```python
class BuildRequiresTest(unittest.TestCase):
    def test_consumer(self):
        # https://github.com/conan-io/conan/issues/5425
        t = TestClient()
        t.save({"conanfile.py": str(TestConanFile("ToolB", "0.1"))})
        t.run("create . ToolB/0.1@user/testing")
        t.save({"conanfile.py": str(TestConanFile("LibA", "0.1",
                                                  build_requires=["ToolB/0.1@user/testing"]))})
        t.run("create . LibA/0.1@user/testing")
        t.save({"conanfile.py": str(TestConanFile("LibC", "0.1",
                                                  requires=["LibA/0.1@user/testing"],
                                                  build_requires=["ToolB/0.1@user/testing"]))})
        t.run("install .")
        self.assertIn("ToolB/0.1@user/testing from local cache", t.out)
```
As you can see, the build require to ToolB is not being skipped. Could you please double check it? Maybe a more complete and reproducible case would help. Thanks!
I am sorry, LibA is private_requires ToolB. I modified the test case:
```
class BuildRequiresTest(unittest.TestCase):
    def test_consumer(self):
        # https://github.com/conan-io/conan/issues/5425
        t = TestClient()
        t.save({"conanfile.py": str(TestConanFile("ToolB", "0.1"))})
        t.run("create . ToolB/0.1@user/testing")
        t.save({"conanfile.py": str(TestConanFile("LibA", "0.1",
                                                  private_requires=[("ToolB/0.1@user/testing")]))})
        t.run("create . LibA/0.1@user/testing")
        t.save({"conanfile.py": str(TestConanFile("LibC", "0.1",
                                                  requires=[
                                                      "LibA/0.1@user/testing"],
                                                  build_requires=["ToolB/0.1@user/testing"]))})
        t.run("install .")
        self.assertIn("ToolB/0.1@user/testing from local cache", t.out)
```
I try the test case and it is passed. However, In my project `XXX`, it did print `ToolB/0.1@user/testing from local cache`, but the conanbuildinfo.txt has not any info about the `ToolB`. Here is the `conan install` output:
```
conanfile.py (XXX/None@None/None): Installing package
Requirements
    catch2/2.4.2@bincrafters/stable from 'conan-local' - Cache
    fmt/5.2.1@bincrafters/stable from 'conan-local' - Cache
    xxx_logger/1.2.13@screenshare/stable from 'conan-local' - Cache
    spdlog/1.2.1@bincrafters/stable from 'conan-local' - Cache
Packages
    catch2/2.4.2@bincrafters/stable:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9 - Skip
    fmt/5.2.1@bincrafters/stable:038f8796e196b3dba76fcc5fd4ef5d3d9c6866ec - Cache
    xxx_logger/1.2.13@screenshare/stable:aa971e8736e335273eb99282f27319bdaa20df9d - Cache
    spdlog/1.2.1@bincrafters/stable:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9 - Cache
Build requirements
    catch2/2.4.2@bincrafters/stable from 'conan-local' - Cache
Build requirements packages
    catch2/2.4.2@bincrafters/stable:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9 - Skip

fmt/5.2.1@bincrafters/stable: Already installed!
spdlog/1.2.1@bincrafters/stable: Already installed!
xxx_logger/1.2.13@screenshare/stable: Already installed!
``` 
catch2 -> ToolB
xxx_logger -> LibA
XXX -> LibC

here is the conanbuildinfo.txt.
```
[includedirs]
/home/xyz1001/.conan/data/xxx_logger/1.2.13/screenshare/stable/package/aa971e8736e335273eb99282f27319bdaa20df9d/include
/home/xyz1001/.conan/data/spdlog/1.2.1/bincrafters/stable/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include
/home/xyz1001/.conan/data/fmt/5.2.1/bincrafters/stable/package/038f8796e196b3dba76fcc5fd4ef5d3d9c6866ec/include

[libdirs]
/home/xyz1001/.conan/data/xxx_logger/1.2.13/screenshare/stable/package/aa971e8736e335273eb99282f27319bdaa20df9d/lib
/home/xyz1001/.conan/data/spdlog/1.2.1/bincrafters/stable/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/lib
/home/xyz1001/.conan/data/fmt/5.2.1/bincrafters/stable/package/038f8796e196b3dba76fcc5fd4ef5d3d9c6866ec/lib

[bindirs]


[resdirs]


[builddirs]
/home/xyz1001/.conan/data/xxx_logger/1.2.13/screenshare/stable/package/aa971e8736e335273eb99282f27319bdaa20df9d/
/home/xyz1001/.conan/data/spdlog/1.2.1/bincrafters/stable/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/
/home/xyz1001/.conan/data/fmt/5.2.1/bincrafters/stable/package/038f8796e196b3dba76fcc5fd4ef5d3d9c6866ec/

[libs]
xxx_logger
pthread
fmtd

[defines]
SPDLOG_FMT_EXTERNAL

[cppflags]


[cxxflags]


[cflags]


[sharedlinkflags]


[exelinkflags]


[sysroot]



[includedirs_xxx_logger]
/home/xyz1001/.conan/data/xxx_logger/1.2.13/screenshare/stable/package/aa971e8736e335273eb99282f27319bdaa20df9d/include

[libdirs_xxx_logger]
/home/xyz1001/.conan/data/xxx_logger/1.2.13/screenshare/stable/package/aa971e8736e335273eb99282f27319bdaa20df9d/lib

[bindirs_xxx_logger]


[resdirs_xxx_logger]


[builddirs_xxx_logger]
/home/xyz1001/.conan/data/xxx_logger/1.2.13/screenshare/stable/package/aa971e8736e335273eb99282f27319bdaa20df9d/

[libs_xxx_logger]
xxx_logger
pthread

[defines_xxx_logger]


[cppflags_xxx_logger]


[cxxflags_xxx_logger]


[cflags_xxx_logger]


[sharedlinkflags_xxx_logger]


[exelinkflags_xxx_logger]


[sysroot_xxx_logger]


[rootpath_xxx_logger]
/home/xyz1001/.conan/data/xxx_logger/1.2.13/screenshare/stable/package/aa971e8736e335273eb99282f27319bdaa20df9d


[includedirs_spdlog]
/home/xyz1001/.conan/data/spdlog/1.2.1/bincrafters/stable/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include

[libdirs_spdlog]
/home/xyz1001/.conan/data/spdlog/1.2.1/bincrafters/stable/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/lib

[bindirs_spdlog]


[resdirs_spdlog]


[builddirs_spdlog]
/home/xyz1001/.conan/data/spdlog/1.2.1/bincrafters/stable/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/

[libs_spdlog]
pthread

[defines_spdlog]
SPDLOG_FMT_EXTERNAL

[cppflags_spdlog]


[cxxflags_spdlog]


[cflags_spdlog]


[sharedlinkflags_spdlog]


[exelinkflags_spdlog]


[sysroot_spdlog]


[rootpath_spdlog]
/home/xyz1001/.conan/data/spdlog/1.2.1/bincrafters/stable/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9


[includedirs_fmt]
/home/xyz1001/.conan/data/fmt/5.2.1/bincrafters/stable/package/038f8796e196b3dba76fcc5fd4ef5d3d9c6866ec/include

[libdirs_fmt]
/home/xyz1001/.conan/data/fmt/5.2.1/bincrafters/stable/package/038f8796e196b3dba76fcc5fd4ef5d3d9c6866ec/lib

[bindirs_fmt]


[resdirs_fmt]


[builddirs_fmt]
/home/xyz1001/.conan/data/fmt/5.2.1/bincrafters/stable/package/038f8796e196b3dba76fcc5fd4ef5d3d9c6866ec/

[libs_fmt]
fmtd

[defines_fmt]


[cppflags_fmt]


[cxxflags_fmt]


[cflags_fmt]


[sharedlinkflags_fmt]


[exelinkflags_fmt]


[sysroot_fmt]


[rootpath_fmt]
/home/xyz1001/.conan/data/fmt/5.2.1/bincrafters/stable/package/038f8796e196b3dba76fcc5fd4ef5d3d9c6866ec


[USER_xxx_logger]
[USER_spdlog]
[USER_fmt]
[ENV_xxx_logger]
[ENV_spdlog]
[ENV_fmt]

```
Confirmed this is an unfortunate bug, coming from a mixture of build-requirements and private requirements. It seems not trivial, it would take some time to fix.

In the meanwhile, I would strongly suggest to reconsider the usage of ``private`` requirements. We are discouraging its use (as you can see they are barely documented), should be only for some extreme cases, like needing to wrap 2 different versions of the same library. What would be the case of ``private`` requirement of ``catch`` library?



Resolving patch:
diff --git a/conans/client/graph/graph_binaries.py b/conans/client/graph/graph_binaries.py
--- a/conans/client/graph/graph_binaries.py
+++ b/conans/client/graph/graph_binaries.py
@@ -39,7 +39,6 @@ def _evaluate_node(self, node, build_mode, update, evaluated_nodes, remotes):
             return
 
         ref, conanfile = node.ref, node.conanfile
-        pref = node.pref
         # If it has lock
         locked = node.graph_lock_node
         if locked and locked.pref.id == node.package_id:
@@ -53,7 +52,13 @@ def _evaluate_node(self, node, build_mode, update, evaluated_nodes, remotes):
         if previous_nodes:
             previous_nodes.append(node)
             previous_node = previous_nodes[0]
-            node.binary = previous_node.binary
+            # The previous node might have been skipped, but current one not necessarily
+            # keep the original node.binary value (before being skipped), and if it will be
+            # defined as SKIP again by self._handle_private(node) if it is really private
+            if previous_node.binary == BINARY_SKIP:
+                node.binary = previous_node.binary_non_skip
+            else:
+                node.binary = previous_node.binary
             node.binary_remote = previous_node.binary_remote
             node.prev = previous_node.prev
             return
@@ -229,6 +234,8 @@ def _handle_private(self, node):
                 # Current closure contains own node to be skipped
                 for n in neigh.public_closure.values():
                     if n.private:
+                        # store the binary origin before being overwritten by SKIP
+                        n.binary_non_skip = n.binary
                         n.binary = BINARY_SKIP
                         self._handle_private(n)
 


Test patch:
diff --git a/conans/test/functional/build_requires/build_requires_test.py b/conans/test/functional/build_requires/build_requires_test.py
--- a/conans/test/functional/build_requires/build_requires_test.py
+++ b/conans/test/functional/build_requires/build_requires_test.py
@@ -51,6 +51,29 @@ def build_requirements(self):
 
 
 class BuildRequiresTest(unittest.TestCase):
+
+    def test_consumer(self):
+        # https://github.com/conan-io/conan/issues/5425
+        t = TestClient()
+        t.save({"conanfile.py": str(TestConanFile("catch", "0.1", info=True))})
+        t.run("create . catch/0.1@user/testing")
+        t.save({"conanfile.py": str(TestConanFile("LibA", "0.1",
+                                                  private_requires=["catch/0.1@user/testing"]))})
+        t.run("create . LibA/0.1@user/testing")
+        t.save({"conanfile.py": str(TestConanFile("LibC", "0.1",
+                                                  requires=["LibA/0.1@user/testing"],
+                                                  build_requires=["catch/0.1@user/testing"]))})
+        t.run("install .")
+        self.assertIn("catch/0.1@user/testing from local cache", t.out)
+        self.assertIn("catch/0.1@user/testing:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9 - Skip",
+                      t.out)
+        self.assertIn("catch/0.1@user/testing:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9 - Cache",
+                      t.out)
+        conanbuildinfo = load(os.path.join(t.current_folder, "conanbuildinfo.txt"))
+        self.assertIn('MYENV=["myenvcatch0.1env"]', conanbuildinfo)
+        self.assertIn('[libs_catch]', conanbuildinfo)
+        self.assertIn("mylibcatch0.1lib", conanbuildinfo)
+
     def test_build_requires_diamond(self):
         t = TestClient()
         t.save({"conanfile.py": str(TestConanFile("libA", "0.1"))})