nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: conan-io/conan
Pull request #17503 (2024-12-18 13:58:37)

Don't use a reference for new component properties lists when merging
Changelog: Bugfix: Fix bogus duplication of component properties
Docs: Omit

Found as the root cause of https://github.com/conan-io/conan-center-index/issues/26213

This caused component properties to be broken when merging lists, as the first component would get the rest of the values that ended up being set

Closes https://github.com/conan-io/conan-center-index/issues/26213


Patch:
diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index 98ea5aae6..57b619dab 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -451,7 +451,7 @@ class _Component:
                 if existing is not None and isinstance(existing, list) and not overwrite:
                     existing.extend(v)
                 else:
-                    current_values[k] = v
+                    current_values[k] = copy.copy(v)
 
     def set_relative_base_folder(self, folder):
         for varname in _DIRS_VAR_NAMES:


Test patch:
diff --git a/test/integration/test_components.py b/test/integration/test_components.py
index 5506fd276..cbe29f118 100644
--- a/test/integration/test_components.py
+++ b/test/integration/test_components.py
@@ -149,3 +149,43 @@ def test_components_overrides():
     # This used to crash, because override was not correctly excluded
     c.run("create app")
     assert "app/0.1: Created package" in c.out
+
+def test_duplication_component_properties():
+    """ Regression for PR 17503 - component lists would be incorrectly aggregated """
+    tc = TestClient(light=True)
+
+    dep = textwrap.dedent("""
+    from conan import ConanFile
+
+    class Dep(ConanFile):
+        name = "dep"
+        version = "0.1"
+
+        def package_info(self):
+            self.cpp_info.components["acomp"].set_property("prop_list", ["value1"])
+            self.cpp_info.components["bcomp"].set_property("prop_list", ["value2"])
+            self.cpp_info.components["ccomp"].set_property("prop_list", ["value3"])
+    """)
+
+    conanfile = textwrap.dedent("""
+    from conan import ConanFile
+
+    class Pkg(ConanFile):
+        name = "pkg"
+        version = "0.1"
+        requires = "dep/0.1"
+
+        def generate(self):
+            # Calling this would break property lists of the last lex sorted component
+            aggregated_components = self.dependencies["dep"].cpp_info.aggregated_components()
+            ccomp = self.dependencies["dep"].cpp_info.components["ccomp"]
+            self.output.info("ccomp list: " + str(ccomp.get_property("prop_list")))
+    """)
+
+    tc.save({"dep/conanfile.py": dep,
+             "conanfile.py": conanfile})
+    tc.run("create dep")
+    tc.run("create .")
+    # The bug would give ccomp the prop_list values of the other two components
+    assert "pkg/0.1: ccomp list: ['value3', 'value2', 'value1']" not in tc.out
+    assert "pkg/0.1: ccomp list: ['value3']" in tc.out
Repository: conan-io/conan
Pull request #17512 (2024-12-19 13:53:38)

Guard against unknown languages attribute values
Changelog: Fix: Error out when unknown language is used in languages attribute.
Docs: Omit

This ensures only known languages are used - Helpful when typing lowercase versions or variants of C++ by mistake. In case of C for example, this would lead to the cppstd setting unexpectedly affecting the pkgid, for which there was no warn before this

The only drawback for this is that it eliminates potential custom usages of the attribute in user customizations, but those can be solved by using any other user-specific attribute if needed


Patch:
diff --git a/conans/model/conan_file.py b/conans/model/conan_file.py
index 27001f20f..ac8418515 100644
--- a/conans/model/conan_file.py
+++ b/conans/model/conan_file.py
@@ -95,6 +95,8 @@ class ConanFile:
             self.generators = [self.generators]
         if isinstance(self.languages, str):
             self.languages = [self.languages]
+        if not all(lang == "C" or lang == "C++" for lang in self.languages):
+            raise ConanException("Only 'C' and 'C++' languages are allowed in 'languages' attribute")
         if isinstance(self.settings, str):
             self.settings = [self.settings]
         self.requires = Requirements(self.requires, self.build_requires, self.test_requires,


Test patch:
diff --git a/test/integration/conanfile/conanfile_errors_test.py b/test/integration/conanfile/conanfile_errors_test.py
index 5c59e782d..057589595 100644
--- a/test/integration/conanfile/conanfile_errors_test.py
+++ b/test/integration/conanfile/conanfile_errors_test.py
@@ -240,3 +240,22 @@ def test_consumer_unexpected():
     tc.run("create cmake")
     tc.run("create . -b=missing -pr=profile")
     assert "cmake/1.0 -> True" not in tc.out
+
+
+@pytest.mark.parametrize("languages", [
+    "['C', 'CXX']",
+    "['CXX']",
+    "'CXX'",
+])
+def test_language_unexpected(languages):
+    tc = TestClient(light=True)
+    tc.save({"conanfile.py": GenConanfile().with_class_attribute(f"languages = {languages}")})
+    tc.run("inspect .", assert_error=True)
+    assert "Only 'C' and 'C++' languages are allowed in 'languages' attribute" in tc.out
+
+
+def test_empty_languages():
+    tc = TestClient(light=True)
+    tc.save({"conanfile.py": GenConanfile().with_class_attribute("languages = []")})
+    tc.run("inspect .")
+    assert "Only 'C' and 'C++' languages are allowed in 'languages' attribute" not in tc.out
Repository: conan-io/conan
Pull request #17532 (2024-12-27 12:40:38)

GnuToolchain's make_args handle empty values correctly
Changelog: Fix: GnuToolchain's make_args handle empty values correctly
Docs: Omit

Close: https://github.com/conan-io/conan/issues/17171





Patch:
diff --git a/conan/tools/gnu/gnutoolchain.py b/conan/tools/gnu/gnutoolchain.py
index 336d8e06a..6407f5c30 100644
--- a/conan/tools/gnu/gnutoolchain.py
+++ b/conan/tools/gnu/gnutoolchain.py
@@ -237,7 +237,7 @@ class GnuToolchain:
 
     @staticmethod
     def _dict_to_list(flags):
-        return [f"{k}={v}" if v else k for k, v in flags.items()]
+        return [f"{k}={v}" if v is not None else k for k, v in flags.items()]
 
     @property
     def cxxflags(self):


Test patch:
diff --git a/test/unittests/tools/gnu/test_gnutoolchain.py b/test/unittests/tools/gnu/test_gnutoolchain.py
index cf053530e..c53f9d7bb 100644
--- a/test/unittests/tools/gnu/test_gnutoolchain.py
+++ b/test/unittests/tools/gnu/test_gnutoolchain.py
@@ -192,5 +192,9 @@ def test_update_or_prune_any_args(cross_building_conanfile):
     assert "'--force" not in new_autoreconf_args
     # Add new value to make_args
     at.make_args.update({"--new-complex-flag": "new-value"})
+    at.make_args.update({"--new-empty-flag": ""})
+    at.make_args.update({"--new-no-value-flag": None})
     new_make_args = cmd_args_to_string(GnuToolchain._dict_to_list(at.make_args))
     assert "--new-complex-flag=new-value" in new_make_args
+    assert "--new-empty-flag=" in new_make_args
+    assert "--new-no-value-flag" in new_make_args and "--new-no-value-flag=" not in new_make_args
Repository: conan-io/conan
Pull request #17571 (2025-01-14 13:25:36)

Updated to use maxsplit=1
Changelog: Fix: Fix `Git` `is_dirty` detection of excluded files with paths
Docs: Omit


I have a file that has a space in its name. In the conan export method I use it like this:
```
def export(self):
        git = Git(self, self.recipe_folder, excluded=["src/file Component Toolchain.prj"])
        git.coordinates_to_conandata()
```
 and this fails because of the split:
```
lines = [line.split()[1] for line in lines if line]
```

Closes https://github.com/conan-io/conan/issues/17570



- [X] Refer to the issue that supports this Pull Request. https://github.com/conan-io/conan/issues/17570
- [ ] If the issue has missing info, explain the purpose/use case/pain/need that covers this Pull Request.
- [x] I've read the [Contributing guide](https://github.com/conan-io/conan/blob/develop2/.github/CONTRIBUTING.md).
- [x] I've followed the PEP8 style guides for Python code.
- [ ] I've opened another PR in the Conan docs repo to the ``develop`` branch, documenting this one.



Patch:
diff --git a/conan/tools/scm/git.py b/conan/tools/scm/git.py
index f3578e2c9..ee259655b 100644
--- a/conan/tools/scm/git.py
+++ b/conan/tools/scm/git.py
@@ -137,7 +137,9 @@ class Git:
             return bool(status)
         # Parse the status output, line by line, and match it with "_excluded"
         lines = [line.strip() for line in status.splitlines()]
-        lines = [line.split()[1] for line in lines if line]
+        # line is of the form STATUS PATH, get the path by splitting
+        # (Taking into account that STATUS is one word, PATH might be many)
+        lines = [line.split(maxsplit=1)[1].strip('"') for line in lines if line]
         lines = [line for line in lines if not any(fnmatch.fnmatch(line, p) for p in self._excluded)]
         self._conanfile.output.debug(f"Filtered git status: {lines}")
         return bool(lines)


Test patch:
diff --git a/test/functional/tools/scm/test_git.py b/test/functional/tools/scm/test_git.py
index 7c8df1697..d4211ec68 100644
--- a/test/functional/tools/scm/test_git.py
+++ b/test/functional/tools/scm/test_git.py
@@ -26,7 +26,7 @@ class TestGitBasicCapture:
             version = "0.1"
 
             def export(self):
-                git = Git(self, self.recipe_folder, excluded=["myfile.txt", "mynew.txt"])
+                git = Git(self, self.recipe_folder, excluded=["myfile.txt", "mynew.txt", "file with spaces.txt"])
                 commit = git.get_commit()
                 repo_commit = git.get_commit(repository=True)
                 url = git.get_remote_url()
@@ -127,7 +127,8 @@ class TestGitBasicCapture:
         c.run("export . -vvv")
         assert "pkg/0.1: DIRTY: False" in c.out
         c.save({"myfile.txt": "changed",
-                "mynew.txt": "new"})
+                "mynew.txt": "new",
+                "file with spaces.txt": "hello"})
         c.run("export .")
         assert "pkg/0.1: DIRTY: False" in c.out
         c.save({"other.txt": "new"})
Repository: conan-io/conan
Pull request #17649 (2025-01-27 18:37:42)

Fix `conan config clean` not resetting default file structure
Changelog: Bugfix: Fix `conan config clean` not regenerating every necessary file
Docs: Omit



Patch:
diff --git a/conan/api/subapi/config.py b/conan/api/subapi/config.py
index 0267766a1..5f4abcb65 100644
--- a/conan/api/subapi/config.py
+++ b/conan/api/subapi/config.py
@@ -208,7 +208,7 @@ class ConfigAPI:
         packages_folder = self.global_conf.get("core.cache:storage_path") or os.path.join(self.home(), "p")
         for content in contents:
             content_path = os.path.join(self.home(), content)
-            if content_path == packages_folder or content == "version.txt":
+            if content_path == packages_folder:
                 continue
             ConanOutput().debug(f"Removing {content_path}")
             if os.path.isdir(content_path):
diff --git a/conan/cli/commands/config.py b/conan/cli/commands/config.py
index 8454d1401..1f78df37a 100644
--- a/conan/cli/commands/config.py
+++ b/conan/cli/commands/config.py
@@ -138,7 +138,7 @@ def config_show(conan_api, parser, subparser, *args):
 @conan_subcommand()
 def config_clean(conan_api, parser, subparser, *args):
     """
-    Clean the configuration files in the Conan home folder. (Keeping installed packages)
+    (Experimental) Clean the configuration files in the Conan home folder, while keeping installed packages
     """
     parser.parse_args(*args)
     conan_api.config.clean()


Test patch:
diff --git a/test/integration/command/config_test.py b/test/integration/command/config_test.py
index c84ea3e36..55a34909b 100644
--- a/test/integration/command/config_test.py
+++ b/test/integration/command/config_test.py
@@ -232,8 +232,14 @@ def test_config_clean(storage_path):
     assert "bar" not in tc.out
     tc.run("config show core.upload:retry")
     assert "7" not in tc.out
-    assert not os.path.exists(os.path.join(tc.cache_folder, "extensions"))
+    assert os.path.exists(os.path.join(tc.cache_folder, "extensions"))
+    assert not os.path.exists(os.path.join(tc.cache_folder, "extensions", "compatibility", "mycomp.py"))
     assert os.path.exists(absolut_storage_path)
+    # This will error because the call to clean will remove the profiles
+    tc.run("create .", assert_error=True)
+    # Works after regenerating them!
+    tc.run("profile detect")
+    tc.run("create .")
 
 
 def test_config_reinit():
Repository: conan-io/conan
Pull request #17692 (2025-02-04 09:10:14)

Protect access to CppInfo private property required_components
Changelog: Fix: Protect erroneous assignment of ``cpp_info/components.required_components = xxx``, for ``required_components`` property. Now it will raise a proper error.
Docs: omit

Regarding the issue of CCI, one recipe was overriding the internal property `required_components`. This should have triggered an exception beforehand.
 
https://github.com/conan-io/conan-center-index/issues/26455

- [x] Refer to the issue that supports this Pull Request.
- [x] If the issue has missing info, explain the purpose/use case/pain/need that covers this Pull Request.
- [x] I've read the [Contributing guide](https://github.com/conan-io/conan/blob/develop2/.github/CONTRIBUTING.md).
- [x] I've followed the PEP8 style guides for Python code.
- [ ] I've opened another PR in the Conan docs repo to the ``develop`` branch, documenting this one.



Patch:
diff --git a/conan/internal/model/cpp_info.py b/conan/internal/model/cpp_info.py
index 05f9af4c2..05e982545 100644
--- a/conan/internal/model/cpp_info.py
+++ b/conan/internal/model/cpp_info.py
@@ -649,7 +649,7 @@ class CppInfo:
         return getattr(self._package, attr)
 
     def __setattr__(self, attr, value):
-        if attr in ("components", "default_components", "_package", "_aggregated"):
+        if attr in ("components", "default_components", "_package", "_aggregated", "required_components"):
             super(CppInfo, self).__setattr__(attr, value)
         else:
             setattr(self._package, attr, value)


Test patch:
diff --git a/test/unittests/model/build_info/components_test.py b/test/unittests/model/build_info/components_test.py
index ff4c2259b..28ed5bc55 100644
--- a/test/unittests/model/build_info/components_test.py
+++ b/test/unittests/model/build_info/components_test.py
@@ -15,6 +15,9 @@ class CppInfoComponentsTest(unittest.TestCase):
         self.assertListEqual(cpp_info.components["libb"].includedirs, ["include", "includewhat"])
         self.assertListEqual(cpp_info.components["libc"].libs, ["thelibc"])
 
+        with self.assertRaises(AttributeError):
+            cpp_info.required_components = ["liba::liba"]
+
     def test_no_components_inside_components(self):
         cpp_info = CppInfo()
         with self.assertRaises(AttributeError):
Repository: conan-io/conan
Pull request #17821 (2025-02-20 12:39:30)

improve message for new CMakeDeps
Changelog: Fix: Better message for incubating CMakeDeps about ``target_link_libraries()`` from tool-requires
Docs: Omit



Patch:
diff --git a/conan/tools/cmake/cmakedeps2/cmakedeps.py b/conan/tools/cmake/cmakedeps2/cmakedeps.py
index e190c268a..cae3326c0 100644
--- a/conan/tools/cmake/cmakedeps2/cmakedeps.py
+++ b/conan/tools/cmake/cmakedeps2/cmakedeps.py
@@ -61,7 +61,7 @@ class CMakeDeps2:
                 continue
 
             if require.direct:
-                direct_deps.append(dep)
+                direct_deps.append((require, dep))
             config = ConfigTemplate2(self, dep)
             ret[config.filename] = config.content()
             config_version = ConfigVersionTemplate2(self, dep)
@@ -78,14 +78,16 @@ class CMakeDeps2:
     def _print_help(self, direct_deps):
         if direct_deps:
             msg = ["CMakeDeps necessary find_package() and targets for your CMakeLists.txt"]
-            targets = []
-            for dep in direct_deps:
-                msg.append(f"    find_package({self.get_cmake_filename(dep)})")
-                if not dep.cpp_info.exe:
+            link_targets = []
+            for (require, dep) in direct_deps:
+                note = " # Optional. This is a tool-require, can't link its targets" \
+                    if require.build else ""
+                msg.append(f"    find_package({self.get_cmake_filename(dep)}){note}")
+                if not require.build and not dep.cpp_info.exe:
                     target_name = self.get_property("cmake_target_name", dep)
-                    targets.append(target_name or f"{dep.ref.name}::{dep.ref.name}")
-            if targets:
-                msg.append(f"    target_link_libraries(... {' '.join(targets)})")
+                    link_targets.append(target_name or f"{dep.ref.name}::{dep.ref.name}")
+            if link_targets:
+                msg.append(f"    target_link_libraries(... {' '.join(link_targets)})")
             self._conanfile.output.info("\n".join(msg), fg=Color.CYAN)
 
     def set_property(self, dep, prop, value, build_context=False):


Test patch:
diff --git a/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_new.py b/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_new.py
index 64a993e5e..27a7184cb 100644
--- a/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_new.py
+++ b/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_new.py
@@ -868,6 +868,9 @@ class TestToolRequires:
                                                               .with_tool_requires("tool/0.1")})
         c.run("create tool")
         c.run(f"install pkg -g CMakeDeps -c tools.cmake.cmakedeps:new={new_value}")
+        assert "find_package(tool) # Optional. This is a tool-require, " \
+               "can't link its targets" in c.out
+        assert "target_link_libraries" not in c.out
         tool_config = c.load("pkg/tool-config.cmake")
         assert 'set(tool_INCLUDE_DIRS' not in tool_config
         assert 'set(tool_INCLUDE_DIR' not in tool_config
Repository: conan-io/conan
Pull request #17954 (2025-03-12 13:27:21)

custom generator class serialization fix
Changelog: Bugfix: Avoid crash of ``--format=json`` serialization when custom generators inside tool-requires are referenced by class, not by name
Docs: Omit

Close https://github.com/conan-io/conan/issues/17948



Patch:
diff --git a/conan/internal/model/conan_file.py b/conan/internal/model/conan_file.py
index 58fb90754..34ef337cc 100644
--- a/conan/internal/model/conan_file.py
+++ b/conan/internal/model/conan_file.py
@@ -150,7 +150,8 @@ class ConanFile:
         result["options_definitions"] = self.options.possible_values
 
         if self.generators is not None:
-            result["generators"] = list(self.generators)
+            result["generators"] = list(s.__name__ if isinstance(s, type) else s
+                                        for s in self.generators)
         if self.license is not None:
             result["license"] = list(self.license) if not isinstance(self.license, str) else self.license
 


Test patch:
diff --git a/test/integration/generators/test_generators_from_br.py b/test/integration/generators/test_generators_from_br.py
index a0db007c4..c0fa619fd 100644
--- a/test/integration/generators/test_generators_from_br.py
+++ b/test/integration/generators/test_generators_from_br.py
@@ -1,3 +1,4 @@
+import json
 import textwrap
 
 from conan.test.utils.tools import TestClient
@@ -89,3 +90,30 @@ def test_inject_vars():
     content = tc.load("conanbuild.sh")
     assert "conantest.sh" in content
     assert "envars_generator2.sh" in content
+
+
+def test_json_serialization_error():
+    c = TestClient()
+    conanfile = textwrap.dedent("""
+        from conan import ConanFile
+
+        class MyGenerator:
+            def __init__(self, conanfile):
+                self._conanfile = conanfile
+
+            def generate(self):
+                pass
+
+        class MyToolReq(ConanFile):
+            name = "mygenerator-tool"
+            version = "1.0"
+
+            def package_info(self):
+                self.generator_info = [MyGenerator]
+        """)
+
+    c.save({"tool/conanfile.py": conanfile})
+    c.run("create tool")
+    c.run("install --tool-requires=mygenerator-tool/1.0 --format=json")
+    graph = json.loads(c.stdout)
+    assert graph["graph"]["nodes"]["0"]["generators"] == ["MyGenerator"]
Repository: conan-io/conan
Pull request #18303 (2025-05-14 12:36:13)

Add clearer message when missing some keys in graph file
Changelog: Fix: Better error message in `conan list --graph=file.json` when using filtered graph
Docs: Omit

Closes https://github.com/conan-io/conan/issues/18302


Patch:
diff --git a/conan/api/model/list.py b/conan/api/model/list.py
index 8c5663421..8af13aba1 100644
--- a/conan/api/model/list.py
+++ b/conan/api/model/list.py
@@ -94,6 +94,10 @@ class MultiPackagesList:
             return mpkglist
         except JSONDecodeError as e:
             raise ConanException(f"Graph file invalid JSON: {graphfile}\n{e}")
+        except KeyError as e:
+            raise ConanException(f'Graph file {graphfile} is missing the required "{e}" key in its contents.\n'
+                                 "Note that the graph file should not be filtered "
+                                 "if you expect to use it with the list command.")
         except ConanException as e:
             raise e
         except Exception as e:


Test patch:
diff --git a/test/integration/command/list/test_combined_pkglist_flows.py b/test/integration/command/list/test_combined_pkglist_flows.py
index 4e78dd87a..6eff4ebc7 100644
--- a/test/integration/command/list/test_combined_pkglist_flows.py
+++ b/test/integration/command/list/test_combined_pkglist_flows.py
@@ -437,3 +437,13 @@ class TestListGraphContext:
             assert content["default"][ref]["revisions"]
         for ref in not_refs:
             assert content["default"].get(ref) is None
+
+    def test_graph_list(self):
+        tc = TestClient(light=True)
+        tc.save({"conanfile.py": GenConanfile("lib", "1.0")})
+
+        tc.run("graph info . -f json --build=never --no-remote --filter=context", redirect_stdout="graph_context.json")
+
+        tc.run("list --graph=graph_context.json --graph-context=build-only --format=json",
+               assert_error=True)
+        assert "Note that the graph file should not be filtered" in tc.out