Repository: conan-io/conan

Issue:
[bug] provide conflict when package depends on itself in build context
### Environment details

* Operating System+version: macos 14.2
* Compiler+version: apple-clang 15.0.0
* Conan version: 2.0.17
* Python version: 3.11


### Steps to reproduce

- Create a package that depends on itself in `build` context (e.g. grpc-style).
- add `provides = 'feature'` attribute to the package's conanfile
- make package editable or export it to the local cache
- install package in cross-compilation mode (i.e. targeting android/iOS/browser)

Expected: this case should work and `provides` attribute should only affect downstream packages

Actual: Package conflicts on itself with error like this:
```
|| ERROR: Provide Conflict: Both 'my_package/2.0.0' and my_package/2.0.0' provide '('pkg1', 'pkg2', 'pkg3')'.
```

The current workaround is to not use the `provides` attribute and rely on c++ compiler/linker errors to detect duplicate symbols.

### Logs

_No response_


Discussion:
Thanks very much for your detailed report @DoDoENT, it helped to identify the issue.

I am submitting a fix in https://github.com/conan-io/conan/pull/15575 for next release.



Patch:
diff --git a/conans/client/graph/provides.py b/conans/client/graph/provides.py
index 8a871fad82a..fd8ab2c8e0d 100644
--- a/conans/client/graph/provides.py
+++ b/conans/client/graph/provides.py
@@ -8,20 +8,25 @@ def check_graph_provides(dep_graph):
     for node in dep_graph.nodes:
         provides = {}
         current_provides = node.conanfile.provides
+        if isinstance(current_provides, str):  # Just in case it is defined in configure() as str
+            current_provides = [current_provides]
         for dep in node.transitive_deps.values():
             dep_node = dep.node
-            dep_require = dep.require
+            if node.ref is not None and dep_node.ref.name == node.ref.name:
+                continue  # avoid dependency to self (as tool-requires for cross-build)
 
             dep_provides = dep_node.conanfile.provides
             if dep_provides is None:
                 continue
+            if isinstance(dep_provides, str):
+                dep_provides = [dep_provides]
             for provide in dep_provides:
                 # First check if collides with current node
                 if current_provides is not None and provide in current_provides:
                     raise GraphProvidesError(node, dep_node)
 
                 # Then, check if collides with other requirements
-                new_req = dep_require.copy_requirement()
+                new_req = dep.require.copy_requirement()
                 new_req.ref = RecipeReference(provide, new_req.ref.version, new_req.ref.user,
                                               new_req.ref.channel)
                 existing = node.transitive_deps.get(new_req)


Test patch:
diff --git a/conans/test/integration/graph/core/test_provides.py b/conans/test/integration/graph/core/test_provides.py
index 464c1179df7..16281faa6c9 100644
--- a/conans/test/integration/graph/core/test_provides.py
+++ b/conans/test/integration/graph/core/test_provides.py
@@ -257,3 +257,27 @@ def package_info(self):
     t.run("install app.py --name=app --version=version")
     t.run("install app.py --name=app --version=version -o app/*:conflict=True", assert_error=True)
     assert "ERROR: Provide Conflict: Both 'app/version' and 'req/v1' provide 'libjpeg'" in t.out
+
+
+def test_self_build_require():
+    c = TestClient()
+    conanfile = textwrap.dedent("""\
+        from conan import ConanFile
+        from conan.tools.build import cross_building
+
+        class Pkg(ConanFile):
+            name = "grpc"
+            version = "0.1"
+            settings = "os"
+            provides = "grpc-impl"
+            def build_requirements(self):
+                if cross_building(self):
+                    self.tool_requires("grpc/0.1")
+        """)
+    c.save({'conanfile.py': conanfile})
+    c.run("create . -s os=Windows -s:b os=Windows")
+    c.assert_listed_binary({"grpc/0.1": ("ebec3dc6d7f6b907b3ada0c3d3cdc83613a2b715", "Build")})
+    c.run("create . -s os=Linux -s:b os=Windows --build=missing")
+    c.assert_listed_binary({"grpc/0.1": ("9a4eb3c8701508aa9458b1a73d0633783ecc2270", "Build")})
+    c.assert_listed_binary({"grpc/0.1": ("ebec3dc6d7f6b907b3ada0c3d3cdc83613a2b715", "Cache")},
+                           build=True)