Repository: conan-io/conan

Issue:
[bug] CONAN_CMAKE_PROGRAM not respected for CMake Toolchain
### Environment Details (include every applicable attribute)
  * Operating System+version:Linux/Windows
  * Compiler+version:gcc/MSVC
  * Conan version:HEAD
  * Python version:3.8

### Steps to reproduce (Include if Applicable)
Have CMake 2.8 installed on your system but also have CMake 3.24 installed to another location.
Specify the CMake version required within CONAN_CMAKE_PROGRAM environment variable.
Try to compile zlib from the conan center recipie. Shouldn't matter the version they have all been converted to use CMake toolchain and specify that CMake minimum version of >2.8.

### Logs (Executed commands with output) (Include/Attach if Applicable)

```
CMake Error at CMakeLists.txt:1 (cmake_minimum_required):
  CMake 3.0 or higher is required.  You are running version 2.8.12.2


-- Configuring incomplete, errors occurred!
```

### Source of issue
https://github.com/conan-io/conan/pull/4298 added this feature back in 2019 but when the CMake Toolchain refactor was done in 2020 https://github.com/conan-io/conan/pull/7076 the #4298 change to cmake program were not ported over. 

Problem line is:
https://github.com/conan-io/conan/blame/develop/conan/tools/cmake/cmake.py#L62

Just needs to be updated to follow 4298:
`self._cmake_program = os.getenv("CONAN_CMAKE_PROGRAM") or cmake_program or "cmake"

I've locally applied this and it fixed my issue.

Of course its completely possible that I am misunderstanding how to set the cmake program path.

Discussion:
CLAassistant: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/conan-io/conan?pullRequest=13940) <br/>Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla-assistant.io/conan-io/conan?pullRequest=13940) before we can accept your contribution.<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/conan-io/conan?pullRequest=13940) it.</sub>
iskunk: All right, I've re-targeted the PR to `release/2.0`, and added my best guess at a unit test for the new configuration item. Please double-check it.

Patch:
diff --git a/conan/cli/formatters/graph/graph.py b/conan/cli/formatters/graph/graph.py
index 3c6ef38df..878a90758 100644
--- a/conan/cli/formatters/graph/graph.py
+++ b/conan/cli/formatters/graph/graph.py
@@ -14,6 +14,8 @@ from conans.client.installer import build_id
 from conans.util.files import load
 
 
+# FIXME: Check all this code when format_graph_[html/dot] use serialized graph
+
 class _PrinterGraphItem(object):
     def __init__(self, _id, node, is_build_time_node):
         self.id = _id
@@ -39,19 +41,13 @@ class _PrinterGraphItem(object):
         return self._is_build_time_node
 
     def data(self):
-
-        def ensure_iterable(value):
-            if isinstance(value, (list, tuple)):
-                return value
-            return value,
-
         return {
             'build_id': build_id(self._conanfile),
             'url': self._conanfile.url,
             'homepage': self._conanfile.homepage,
             'license': self._conanfile.license,
             'author': self._conanfile.author,
-            'topics': ensure_iterable(self._conanfile.topics) if self._conanfile.topics else None
+            'topics': self._conanfile.topics
         }
 
 
diff --git a/conan/cli/formatters/graph/graph_info_text.py b/conan/cli/formatters/graph/graph_info_text.py
index 6c5edc902..2da47f217 100644
--- a/conan/cli/formatters/graph/graph_info_text.py
+++ b/conan/cli/formatters/graph/graph_info_text.py
@@ -6,15 +6,15 @@ from conan.api.output import ConanOutput
 
 def filter_graph(graph, package_filter, field_filter=None):
     if package_filter is not None:
-        graph["nodes"] = [n for n in graph["nodes"]
-                          if any(fnmatch.fnmatch(n["ref"] or "", p) for p in package_filter)]
+        graph["nodes"] = {id_: n for id_, n in graph["nodes"].items()
+                          if any(fnmatch.fnmatch(n["ref"] or "", p) for p in package_filter)}
     if field_filter is not None:
         if "ref" not in field_filter:
             field_filter.append("ref")
-        result = []
-        for n in graph["nodes"]:
+        result = {}
+        for id_, n in graph["nodes"].items():
             new_node = OrderedDict((k, v) for k, v in n.items() if k in field_filter)
-            result.append(new_node)
+            result[id_] = new_node
         graph["nodes"] = result
     return graph
 
@@ -31,7 +31,7 @@ def format_graph_info(result):
     out.title("Basic graph information")
     serial = graph.serialize()
     serial = filter_graph(serial, package_filter, field_filter)
-    for n in serial["nodes"]:
+    for n in serial["nodes"].values():
         out.writeln(f"{n['ref']}:")  # FIXME: This can be empty for consumers and it is ugly ":"
         _serial_pretty_printer(n, indent="  ")
     if graph.error:
diff --git a/conan/tools/cmake/cmake.py b/conan/tools/cmake/cmake.py
index b2cae603e..ab4593e57 100644
--- a/conan/tools/cmake/cmake.py
+++ b/conan/tools/cmake/cmake.py
@@ -49,7 +49,7 @@ class CMake(object):
         self._toolchain_file = configure_preset.get("toolchainFile")
         self._cache_variables = configure_preset["cacheVariables"]
 
-        self._cmake_program = "cmake"  # Path to CMake should be handled by environment
+        self._cmake_program = conanfile.conf.get("tools.cmake:cmake_program", default="cmake")
 
     def configure(self, variables=None, build_script_folder=None, cli_args=None):
         """
diff --git a/conan/tools/premake/premakedeps.py b/conan/tools/premake/premakedeps.py
index a94939a2c..d3eaa3e49 100644
--- a/conan/tools/premake/premakedeps.py
+++ b/conan/tools/premake/premakedeps.py
@@ -83,11 +83,11 @@ end
 
 # Helper class that expands cpp_info meta information in lua readable string sequences
 class _PremakeTemplate(object):
-    def __init__(self, dep_cpp_info, pkg_folder):
+    def __init__(self, dep_cpp_info):
         def _format_paths(paths):
             if not paths:
                 return ""
-            return ",\n".join(f'"{pkg_folder}/{p}"'.replace("\\", "/") for p in paths)
+            return ",\n".join(f'"{p}"'.replace("\\", "/") for p in paths)
 
         def _format_flags(flags):
             if not flags:
@@ -106,7 +106,7 @@ class _PremakeTemplate(object):
         self.exelinkflags = _format_flags(dep_cpp_info.exelinkflags)
         self.frameworks = ", ".join('"%s.framework"' % p.replace('"', '\\"') for p in
                                     dep_cpp_info.frameworks) if dep_cpp_info.frameworks else ""
-        self.sysroot = f"{pkg_folder}/{dep_cpp_info.sysroot}".replace("\\", "/") \
+        self.sysroot = f"{dep_cpp_info.sysroot}".replace("\\", "/") \
             if dep_cpp_info.sysroot else ""
 
 
@@ -211,7 +211,7 @@ class PremakeDeps(object):
             var_filename = PREMAKE_VAR_FILE.format(pkgname=dep_name, config=conf_suffix)
             self._output_lua_file(var_filename, [
                 PREMAKE_TEMPLATE_VAR.format(pkgname=dep_name,
-                    config=conf_name, deps=_PremakeTemplate(dep_aggregate, dep.package_folder))
+                    config=conf_name, deps=_PremakeTemplate(dep_aggregate))
             ])
 
             # Create list of all available profiles by searching on disk
diff --git a/conans/client/graph/graph.py b/conans/client/graph/graph.py
index 101394301..96170d65b 100644
--- a/conans/client/graph/graph.py
+++ b/conans/client/graph/graph.py
@@ -222,9 +222,11 @@ class Node(object):
         result["info_invalid"] = getattr(getattr(self.conanfile, "info", None), "invalid", None)
         # Adding the conanfile information: settings, options, etc
         result.update(self.conanfile.serialize())
+        result.pop("requires", None)  # superseded by "dependencies" (graph.transitive_deps)
+        result["dependencies"] = {d.node.id: d.require.serialize()
+                                  for d in self.transitive_deps.values() if d.node is not None}
         result["context"] = self.context
         result["test"] = self.test
-        result["requires"] = {n.id: n.ref.repr_notime() for n in self.neighbors()}
         return result
 
     def overrides(self):
@@ -381,6 +383,8 @@ class DepsGraph(object):
         for i, n in enumerate(self.nodes):
             n.id = i
         result = OrderedDict()
-        result["nodes"] = [n.serialize() for n in self.nodes]
+        result["nodes"] = {n.id: n.serialize() for n in self.nodes}
         result["root"] = {self.root.id: repr(self.root.ref)}  # TODO: ref of consumer/virtual
+        result["overrides"] = self.overrides().serialize()
+        result["resolved_ranges"] = {repr(r): s.repr_notime() for r, s in self.resolved_ranges.items()}
         return result
diff --git a/conans/client/installer.py b/conans/client/installer.py
index 56587ee9f..5c26c0d7e 100644
--- a/conans/client/installer.py
+++ b/conans/client/installer.py
@@ -332,8 +332,6 @@ class BinaryInstaller:
             conanfile = n.conanfile
             # Call the info method
             conanfile.folders.set_base_package(pkg_folder)
-            conanfile.folders.set_base_source(None)
-            conanfile.folders.set_base_build(None)
             self._call_package_info(conanfile, pkg_folder, is_editable=False)
 
     def _handle_node_editable(self, install_node):
@@ -402,7 +400,8 @@ class BinaryInstaller:
                 self._hook_manager.execute("pre_package_info", conanfile=conanfile)
 
                 if hasattr(conanfile, "package_info"):
-                    with conanfile_remove_attr(conanfile, ['info'], "package_info"):
+                    with conanfile_remove_attr(conanfile, ['info', "source_folder", "build_folder"],
+                                               "package_info"):
                         MockInfoProperty.package = str(conanfile)
                         conanfile.package_info()
 
diff --git a/conans/model/conan_file.py b/conans/model/conan_file.py
index 2c19df670..104137ee6 100644
--- a/conans/model/conan_file.py
+++ b/conans/model/conan_file.py
@@ -125,14 +125,12 @@ class ConanFile:
         for a in ("name", "user", "channel", "url", "license",
                   "author", "description", "homepage", "build_policy", "upload_policy",
                   "revision_mode", "provides", "deprecated", "win_bash", "win_bash_run",
-                  "default_options", "options_description",):
+                  "default_options", "options_description"):
             v = getattr(self, a, None)
-            if v is not None:
-                result[a] = v
-        if self.version is not None:
-            result["version"] = str(self.version)
-        if self.topics is not None:
-            result["topics"] = list(self.topics)
+            result[a] = v
+
+        result["version"] = str(self.version) if self.version is not None else None
+        result["topics"] = list(self.topics) if self.topics is not None else None
         result["package_type"] = str(self.package_type)
 
         settings = self.settings
diff --git a/conans/model/conf.py b/conans/model/conf.py
index 11cf2fd2e..7336b2e6d 100644
--- a/conans/model/conf.py
+++ b/conans/model/conf.py
@@ -63,6 +63,7 @@ BUILT_IN_CONFS = {
     "tools.cmake.cmaketoolchain:system_processor": "Define CMAKE_SYSTEM_PROCESSOR in CMakeToolchain",
     "tools.cmake.cmaketoolchain:toolset_arch": "Toolset architecture to be used as part of CMAKE_GENERATOR_TOOLSET in CMakeToolchain",
     "tools.cmake.cmake_layout:build_folder_vars": "Settings and Options that will produce a different build folder and different CMake presets names",
+    "tools.cmake:cmake_program": "Path to CMake executable",
     "tools.files.download:retry": "Number of retries in case of failure when downloading",
     "tools.files.download:retry_wait": "Seconds to wait between download attempts",
     "tools.gnu:make_program": "Indicate path to make program",


Test patch:
diff --git a/conans/test/functional/command/test_build.py b/conans/test/functional/command/test_build.py
index 56f7f8960..8a45d026f 100644
--- a/conans/test/functional/command/test_build.py
+++ b/conans/test/functional/command/test_build.py
@@ -64,8 +64,8 @@ def test_build_dots_names():
     client.save({"conanfile.py": conanfile_scope_env}, clean_first=True)
     client.run("build conanfile.py --build=missing")
 
-    assert hellopkg_result["graph"]["nodes"][1]["package_folder"] in client.out
-    assert hellotools_result["graph"]["nodes"][1]["package_folder"] in client.out
+    assert hellopkg_result["graph"]["nodes"]["1"]["package_folder"] in client.out
+    assert hellotools_result["graph"]["nodes"]["1"]["package_folder"] in client.out
 
 
 def test_build_with_deps_env_info():
diff --git a/conans/test/functional/tools/system/package_manager_test.py b/conans/test/functional/tools/system/package_manager_test.py
index 289c656d9..d9b485020 100644
--- a/conans/test/functional/tools/system/package_manager_test.py
+++ b/conans/test/functional/tools/system/package_manager_test.py
@@ -178,7 +178,7 @@ def test_collect_system_requirements():
                    redirect_stdout="graph.json")
     graph = json.loads(client.load("graph.json"))
     assert {"apt-get": {"install": ["pkg1", "pkg2"], "missing": []}} == \
-           graph["graph"]["nodes"][0]["system_requires"]
+           graph["graph"]["nodes"]["0"]["system_requires"]
 
     # plain report, do not check
     client.run("graph info . -c tools.system.package_manager:tool=apt-get "
@@ -186,7 +186,7 @@ def test_collect_system_requirements():
                redirect_stdout="graph2.json")
     graph2 = json.loads(client.load("graph2.json"))
     # TODO: Unify format of ``graph info`` and ``install``
-    assert {"apt-get": {"install": ["pkg1", "pkg2"]}} == graph2["nodes"][0]["system_requires"]
+    assert {"apt-get": {"install": ["pkg1", "pkg2"]}} == graph2["nodes"]["0"]["system_requires"]
 
     # Check report-installed
     with patch.object(_SystemPackageManagerTool, '_conanfile_run', MagicMock(return_value=True)):
@@ -195,7 +195,7 @@ def test_collect_system_requirements():
                    redirect_stdout="graph2.json")
         graph2 = json.loads(client.load("graph2.json"))
         assert {"apt-get": {"install": ["pkg1", "pkg2"],
-                            'missing': ['pkg1', 'pkg2']}} == graph2["nodes"][0]["system_requires"]
+                            'missing': ['pkg1', 'pkg2']}} == graph2["nodes"]["0"]["system_requires"]
 
     # Default "check" will fail, as dpkg-query not installed
     client.run("graph info . -c tools.system.package_manager:tool=apt-get "
diff --git a/conans/test/integration/command/create_test.py b/conans/test/integration/command/create_test.py
index b47248fa8..72716cae5 100644
--- a/conans/test/integration/command/create_test.py
+++ b/conans/test/integration/command/create_test.py
@@ -8,7 +8,7 @@ import pytest
 
 from conans.model.recipe_ref import RecipeReference
 from conans.test.utils.tools import TestClient, NO_SETTINGS_PACKAGE_ID, GenConanfile
-from conans.util.files import load, save
+from conans.util.files import load
 
 
 class CreateTest(unittest.TestCase):
@@ -70,7 +70,6 @@ class MyPkg(ConanFile):
         client.run("create . --name=pkg --version=0.1 --user=lasote")
         assert "pkg/0.1@lasote:" in client.out
 
-
     def test_error_create_name_version(self):
         client = TestClient()
         client.save({"conanfile.py": GenConanfile().with_name("hello").with_version("1.2")})
@@ -87,7 +86,7 @@ class MyPkg(ConanFile):
         self.assertIn("pkg/0.1@lasote/channel: Generating the package", client.out)
         client.run("list * -c")
         self.assertIn("pkg/0.1@lasote/channel", client.out)
-        
+
         # test default without user and channel
         client.run("create . ")
         self.assertIn("pkg/0.1: Generating the package", client.out)
@@ -308,7 +307,7 @@ class MyPkg(ConanFile):
         self.client.save({"conanfile.py": conanfile})
         self.client.run("create . --format=json")
         data = json.loads(self.client.stdout)
-        cpp_info_data = data["graph"]["nodes"][1]["cpp_info"]
+        cpp_info_data = data["graph"]["nodes"]["1"]["cpp_info"]
         self.assertIn("libpkg1", cpp_info_data["pkg1"]["libs"])
         self.assertListEqual([], cpp_info_data["pkg1"]["requires"])
         self.assertIn("libpkg2", cpp_info_data["pkg2"]["libs"])
@@ -370,7 +369,7 @@ def test_create_format_json():
                  ....
                 },
                 {'ref': 'hello/0.1#18d5440ae45afc4c36139a160ac071c7',
-                 'requires': {'2': 'pkg/0.2#db78b8d06a78af5c3ac56706f133098d'},
+                 'dependencies': {'1': {'ref': 'hello/0.1', 'visible': 'True', ...}},
                  ....
                 },
                 {'ref': 'pkg/0.2#44a1a27ac2ea1fbcf434a05c4d57388d',
@@ -437,7 +436,7 @@ def test_create_format_json():
     pkg_pkg_ref = 'pkg/0.2#db78b8d06a78af5c3ac56706f133098d'
     consumer_info = hello_pkg_info = pkg_pkg_info = None
 
-    for n in nodes:
+    for _, n in nodes.items():
         ref = n["ref"]
         if ref == consumer_ref:
             consumer_info = n
@@ -456,13 +455,31 @@ def test_create_format_json():
     assert consumer_info["settings"] == {'arch': 'x86', 'build_type': 'Debug', 'compiler': 'gcc',
                                          'compiler.libcxx': 'libstdc++', 'compiler.version': '12',
                                          'os': 'Linux'}
-    assert consumer_info["requires"] == {'1': hello_pkg_ref}
+    consumer_deps = {
+        '1': {'ref': 'hello/0.1', 'run': 'False', 'libs': 'True', 'skip': 'False',
+              'test': 'False', 'force': 'False', 'direct': 'True', 'build': 'False',
+              'transitive_headers': 'None', 'transitive_libs': 'None', 'headers': 'True',
+              'package_id_mode': 'None', 'visible': 'True'},
+        '2': {'ref': 'pkg/0.2', 'run': 'False', 'libs': 'True', 'skip': 'False', 'test': 'False',
+              'force': 'False', 'direct': 'False', 'build': 'False', 'transitive_headers': 'None',
+              'transitive_libs': 'None', 'headers': 'True', 'package_id_mode': 'None',
+              'visible': 'True'}
+    }
+    assert consumer_info["dependencies"] == consumer_deps
     # hello/0.1 pkg information
     assert hello_pkg_info["package_id"] == "8eba237c0fb239fcb7daa47979ab99258eaaa7d1"
     assert hello_pkg_info["prev"] == "d95380a07c35273509dfc36b26f6cec1"
     assert hello_pkg_info["settings"] == {}
     assert hello_pkg_info["options"] == {}
-    assert hello_pkg_info["requires"] == {'2': pkg_pkg_ref}
+    hello_pkg_info_deps = {
+        "2": {
+            "ref": "pkg/0.2", "run": "False", "libs": "True", "skip": "False", "test": "False",
+            "force": "False", "direct": "True", "build": "False", "transitive_headers": "None",
+            "transitive_libs": "None", "headers": "True", "package_id_mode": "semver_mode",
+            "visible": "True"
+        }
+    }
+    assert hello_pkg_info["dependencies"] == hello_pkg_info_deps
     # pkg/0.2 pkg information
     assert pkg_pkg_info["package_id"] == "fb1439470288b15b2da269ed97b1a5f2f5d1f766"
     assert pkg_pkg_info["prev"] == "6949b0f89941d2a5994f9e6e4a89a331"
@@ -470,7 +487,7 @@ def test_create_format_json():
     assert pkg_pkg_info["settings"] == {'build_type': 'Debug', 'compiler': 'gcc',
                                         'compiler.libcxx': 'libstdc++', 'compiler.version': '12'}
     assert pkg_pkg_info["options"] == {'fPIC': 'True', 'shared': 'False'}
-    assert pkg_pkg_info["requires"] == {}
+    assert pkg_pkg_info["dependencies"] == {}
 
 
 def test_create_format_json_and_deps_cpp_info():
@@ -495,7 +512,7 @@ def test_create_format_json_and_deps_cpp_info():
                           'objects': None,
                           'properties': {'pkg_config_aliases': ['compo1_alias'],
                                          'pkg_config_name': 'compo1'},
-                          'requires': None,
+                          'dependencies': None,
                           'resdirs': None,
                           'sharedlinkflags': None,
                           'srcdirs': None,
@@ -519,7 +536,7 @@ def test_create_format_json_and_deps_cpp_info():
                           'properties': {'pkg_config_aliases': ['pkg_alias1',
                                                                 'pkg_alias2'],
                                          'pkg_config_name': 'pkg_other_name'},
-                          'requires': None,
+                          'dependencies': None,
                           'resdirs': ['/path '
                                       'with '
                                       'spaces/.conan2/p/d15a235e212166d9/p/res'],
@@ -569,7 +586,7 @@ def test_create_format_json_and_deps_cpp_info():
     hello_pkg_ref = 'hello/0.1#18d5440ae45afc4c36139a160ac071c7'
     pkg_pkg_ref = 'pkg/0.2#926714b5fb0a994f47ec37e071eba1da'
     hello_cpp_info = pkg_cpp_info = None
-    for n in nodes:
+    for _, n in nodes.items():
         ref = n["ref"]
         if ref == hello_pkg_ref:
             assert n['binary'] == "Build"
diff --git a/conans/test/integration/command/export_pkg_test.py b/conans/test/integration/command/export_pkg_test.py
index 98ae0fd17..52beb043c 100644
--- a/conans/test/integration/command/export_pkg_test.py
+++ b/conans/test/integration/command/export_pkg_test.py
@@ -297,7 +297,7 @@ class TestConan(ConanFile):
         # Wrong folders
         client.run("export-pkg . --format=json", redirect_stdout="file.json")
         graph = json.loads(client.load("file.json"))
-        assert "pkg/0.1" in graph["graph"]["nodes"][0]["ref"]
+        assert "pkg/0.1" in graph["graph"]["nodes"]["0"]["ref"]
 
     def test_export_pkg_no_ref(self):
         client = TestClient()
@@ -403,7 +403,7 @@ def test_export_pkg_json_formatter():
     hello_pkg_ref = 'hello/0.1#18d5440ae45afc4c36139a160ac071c7'
     pkg_pkg_ref = 'pkg/0.2#926714b5fb0a994f47ec37e071eba1da'
     hello_cpp_info = pkg_cpp_info = None
-    for n in nodes:
+    for _, n in nodes.items():
         ref = n["ref"]
         if ref == hello_pkg_ref:
             assert n['binary'] is None  # The exported package has no binary status
diff --git a/conans/test/integration/command/info/info_test.py b/conans/test/integration/command/info/info_test.py
index c99e733d5..c33e404d0 100644
--- a/conans/test/integration/command/info/info_test.py
+++ b/conans/test/integration/command/info/info_test.py
@@ -70,7 +70,7 @@ class TestBasicCliOutput:
                             """)
         client.save({"conanfile.py": conanfile})
         client.run("graph info . --format=json")
-        recipe = json.loads(client.stdout)["nodes"][0]
+        recipe = json.loads(client.stdout)["nodes"]["0"]
         assert type(recipe["topics"]) == list
         assert recipe["topics"] == ["foo"]
         assert type(recipe["provides"]) == list
@@ -90,7 +90,7 @@ class TestBasicCliOutput:
                     """)
         client2.save({"conanfile.py": conanfile2})
         client2.run("graph info . --format=json")
-        recipe = json.loads(client2.stdout)["nodes"][0]
+        recipe = json.loads(client2.stdout)["nodes"]["0"]
         assert type(recipe["topics"]) == list
         assert recipe["topics"] == ["foo"]
         assert type(recipe["provides"]) == list
@@ -163,10 +163,10 @@ class TestJsonOutput:
         conanfile = GenConanfile("pkg", "0.1").with_setting("build_type")
         client.save({"conanfile.py": conanfile})
         client.run("graph info . --package-filter=nothing --format=json")
-        assert '"nodes": []' in client.out
+        assert '"nodes": {}' in client.out
         client.run("graph info . --package-filter=pkg* --format=json")
         graph = json.loads(client.stdout)
-        assert graph["nodes"][0]["ref"] == "pkg/0.1"
+        assert graph["nodes"]["0"]["ref"] == "pkg/0.1"
 
     def test_json_info_outputs(self):
         client = TestClient()
@@ -174,7 +174,7 @@ class TestJsonOutput:
         client.save({"conanfile.py": conanfile})
         client.run("graph info . -s build_type=Debug --format=json")
         graph = json.loads(client.stdout)
-        assert graph["nodes"][0]["settings"]["build_type"] == "Debug"
+        assert graph["nodes"]["0"]["settings"]["build_type"] == "Debug"
 
 
 class TestAdvancedCliOutput:
@@ -199,7 +199,7 @@ class TestAdvancedCliOutput:
 
         client.run("graph info . --format=json")
         info = json.loads(client.stdout)
-        assert info["nodes"][0]["python_requires"] == ['tool/0.1#4d670581ccb765839f2239cc8dff8fbd']
+        assert info["nodes"]["0"]["python_requires"] == ['tool/0.1#4d670581ccb765839f2239cc8dff8fbd']
 
     def test_build_id_info(self):
         client = TestClient()
diff --git a/conans/test/integration/command/info/test_info_folders.py b/conans/test/integration/command/info/test_info_folders.py
index e834ff66f..e8eda3928 100644
--- a/conans/test/integration/command/info/test_info_folders.py
+++ b/conans/test/integration/command/info/test_info_folders.py
@@ -48,11 +48,11 @@ def test_basic():
     client.run(f"export . --user=user --channel=testing")
     client.run(f"graph info --requires=package/0.1.0@user/testing --format=json")
     nodes = json.loads(client.stdout)["nodes"]
-    assert client.cache_folder in nodes[1]["recipe_folder"]
-    assert os.path.basename(nodes[1]["recipe_folder"]).strip() == EXPORT_FOLDER
-    assert nodes[1]["source_folder"] is None
-    assert nodes[1]["build_folder"] is None
-    assert nodes[1]["package_folder"] is None
+    assert client.cache_folder in nodes["1"]["recipe_folder"]
+    assert os.path.basename(nodes["1"]["recipe_folder"]).strip() == EXPORT_FOLDER
+    assert nodes["1"]["source_folder"] is None
+    assert nodes["1"]["build_folder"] is None
+    assert nodes["1"]["package_folder"] is None
 
 
 def test_build_id():
@@ -86,7 +86,7 @@ def test_deps_basic(client_deps):
         found_ref = False
         assert len(nodes["nodes"]) == 3
 
-        for node in nodes["nodes"]:
+        for _, node in nodes["nodes"].items():
             if node["ref"] == "conanfile":
                 assert node["source_folder"] is None
             else:
@@ -103,22 +103,22 @@ def test_deps_specific_information(client_deps):
     client_deps.run("graph info . --package-filter package/* --format=json")
     nodes = json.loads(client_deps.stdout)["nodes"]
     assert len(nodes) == 1
-    assert "package/0.1.0@user/testing" in nodes[0]["ref"]
-    assert nodes[0]["source_folder"] is None
-    assert nodes[0]["build_folder"] is None
-    assert nodes[0]["package_folder"] is None
+    assert "package/0.1.0@user/testing" in nodes["2"]["ref"]
+    assert nodes["2"]["source_folder"] is None
+    assert nodes["2"]["build_folder"] is None
+    assert nodes["2"]["package_folder"] is None
 
     client_deps.run("graph info . --package-filter package* --format=json")
     nodes = json.loads(client_deps.stdout)["nodes"]
     assert len(nodes) == 2
-    assert "package2/0.2.0@user/testing" in nodes[0]["ref"]
-    assert nodes[0]["source_folder"] is None
-    assert nodes[0]["build_folder"] is None
-    assert nodes[0]["package_folder"] is None
-    assert "package/0.1.0@user/testing" in nodes[1]["ref"]
-    assert nodes[1]["source_folder"] is None
-    assert nodes[1]["build_folder"] is None
-    assert nodes[1]["package_folder"] is None
+    assert "package2/0.2.0@user/testing" in nodes["1"]["ref"]
+    assert nodes["1"]["source_folder"] is None
+    assert nodes["1"]["build_folder"] is None
+    assert nodes["1"]["package_folder"] is None
+    assert "package/0.1.0@user/testing" in nodes["2"]["ref"]
+    assert nodes["2"]["source_folder"] is None
+    assert nodes["2"]["build_folder"] is None
+    assert nodes["2"]["package_folder"] is None
 
 
 def test_single_field():
@@ -128,10 +128,10 @@ def test_single_field():
     client.run(f"graph info --requires package/0.1.0@user/testing --format=json")
     nodes = json.loads(client.stdout)["nodes"]
     assert len(nodes) == 2
-    assert "package/0.1.0@user/testing" in nodes[1]["ref"]
-    assert nodes[1]["source_folder"] is None
-    assert nodes[1]["build_folder"] is None
-    assert nodes[1]["package_folder"] is None
+    assert "package/0.1.0@user/testing" in nodes["1"]["ref"]
+    assert nodes["1"]["source_folder"] is None
+    assert nodes["1"]["build_folder"] is None
+    assert nodes["1"]["package_folder"] is None
 
 
 def test_direct_conanfile():
diff --git a/conans/test/integration/command/install/install_test.py b/conans/test/integration/command/install/install_test.py
index 5bce30c27..cfb48a9ec 100644
--- a/conans/test/integration/command/install/install_test.py
+++ b/conans/test/integration/command/install/install_test.py
@@ -429,7 +429,7 @@ def test_install_json_formatter():
     hello_pkg_ref = 'hello/0.1'  # no revision available
     pkg_pkg_ref = 'pkg/0.2#926714b5fb0a994f47ec37e071eba1da'
     hello_cpp_info = pkg_cpp_info = None
-    for n in nodes:
+    for _, n in nodes.items():
         ref = n["ref"]
         if ref == hello_pkg_ref:
             assert n['binary'] is None
@@ -438,7 +438,7 @@ def test_install_json_formatter():
             assert n['binary'] == "Cache"
             pkg_cpp_info = n['cpp_info']
 
-    hello = nodes[0]
+    hello = nodes["0"]
     assert hello["ref"] == hello_pkg_ref
     assert hello["recipe_folder"] == client.current_folder
     assert hello["build_folder"] == client.current_folder
diff --git a/conans/test/integration/command/test_forced_download_source.py b/conans/test/integration/command/test_forced_download_source.py
index 903678691..c129a06fc 100644
--- a/conans/test/integration/command/test_forced_download_source.py
+++ b/conans/test/integration/command/test_forced_download_source.py
@@ -1,3 +1,5 @@
+import json
+import os
 import textwrap
 
 import pytest
@@ -27,10 +29,16 @@ def client():
 def test_install(client):
     client.run("install --requires=dep/0.1")
     assert "RUNNING SOURCE" not in client.out
-    client.run("install --requires=dep/0.1 -c tools.build:download_source=True")
+    client.run("install --requires=dep/0.1 -c tools.build:download_source=True --format=json")
     assert "RUNNING SOURCE" in client.out
-    client.run("install --requires=dep/0.1 -c tools.build:download_source=True")
+    graph = json.loads(client.stdout)
+    zlib = graph["graph"]["nodes"][1]
+    assert os.path.exists(zlib["source_folder"])
+    client.run("install --requires=dep/0.1 -c tools.build:download_source=True --format=json")
     assert "RUNNING SOURCE" not in client.out
+    graph = json.loads(client.stdout)
+    zlib = graph["graph"]["nodes"][1]
+    assert os.path.exists(zlib["source_folder"])
 
 
 def test_info(client):
diff --git a/conans/test/integration/conanfile/folders_access_test.py b/conans/test/integration/conanfile/folders_access_test.py
index 9ca043744..7c217eb9e 100644
--- a/conans/test/integration/conanfile/folders_access_test.py
+++ b/conans/test/integration/conanfile/folders_access_test.py
@@ -82,8 +82,6 @@ class AConan(ConanFile):
         assert(self.package_folder == os.getcwd())
         assert(isinstance(self.package_path, Path))
         assert(str(self.package_path) == self.package_folder)
-        assert(self.source_folder is None)
-        assert(self.build_folder is None)
 """
 
 
diff --git a/conans/test/integration/graph/test_validate_build.py b/conans/test/integration/graph/test_validate_build.py
index 99d88d2d3..5e263e1d4 100644
--- a/conans/test/integration/graph/test_validate_build.py
+++ b/conans/test/integration/graph/test_validate_build.py
@@ -46,11 +46,11 @@ def test_basic_validate_build_test():
     # What happens with a conan info?
     t.run(f"graph info --requires=foo/1.0 {settings_gcc} --format=json", redirect_stdout="myjson")
     myjson = json.loads(t.load("myjson"))["nodes"]
-    assert myjson[1]["invalid_build"] == "This doesn't build in GCC"
+    assert myjson["1"]["invalid_build"] == "This doesn't build in GCC"
 
     t.run(f"graph info --requires=foo/1.0 {settings_clang} --format=json", redirect_stdout="myjson")
     myjson = json.loads(t.load("myjson"))["nodes"]
-    assert myjson[1]["invalid_build"] is False
+    assert myjson["1"]["invalid_build"] is False
 
 
 def test_with_options_validate_build_test():
diff --git a/conans/test/integration/graph/version_ranges/version_range_override_test.py b/conans/test/integration/graph/version_ranges/version_range_override_test.py
index 377814b11..e13cd51b1 100644
--- a/conans/test/integration/graph/version_ranges/version_range_override_test.py
+++ b/conans/test/integration/graph/version_ranges/version_range_override_test.py
@@ -1,5 +1,5 @@
 # coding=utf-8
-
+import json
 import unittest
 
 import pytest
@@ -127,3 +127,21 @@ class VersionRangeOverrideFailTestCase(unittest.TestCase):
         t.run("create .  --build=missing --build=pkga")
         self.assertIn("ros_core/pr-53@3rdparty/snapshot", t.out)
         self.assertIn("ros_perception/1.1.4@3rdparty/unstable", t.out)
+
+        # Check information got by graph info
+        t.run("graph info . --format json")
+        info = json.loads(t.stdout)
+        expected_overrides = {
+            "ros_core/[~1.1]@3rdparty/unstable": [
+                "ros_core/pr-53@3rdparty/snapshot"
+            ],
+            "ros_core/1.1.4@3rdparty/unstable": [
+                "ros_core/pr-53@3rdparty/snapshot#4d670581ccb765839f2239cc8dff8fbd"
+            ]
+        }
+        self.assertEqual(info["overrides"], expected_overrides)
+        expected_resolved_ranges = {
+            "pkgb/[~0]@common/unstable": "pkgb/0.1@common/unstable",
+            "ros_perception/[~1.1]@3rdparty/unstable": "ros_perception/1.1.4@3rdparty/unstable"
+        }
+        self.assertEqual(info["resolved_ranges"], expected_resolved_ranges)
diff --git a/conans/test/integration/lockfile/test_graph_overrides.py b/conans/test/integration/lockfile/test_graph_overrides.py
index 2727e0d41..cf2b6a9dc 100644
--- a/conans/test/integration/lockfile/test_graph_overrides.py
+++ b/conans/test/integration/lockfile/test_graph_overrides.py
@@ -29,12 +29,14 @@ def test_overrides_half_diamond(override, force):
     assert "pkga/0.2" in requires
     assert "pkga/0.1" not in requires
     c.run("graph info pkgc --lockfile=pkgc/conan.lock --format=json")
-    assert "pkga/0.2" in c.stdout
-    assert "pkga/0.1" not in c.stdout
+    dependencies = json.loads(c.stdout)["nodes"]["0"]["dependencies"]
+    assert "pkga/0.2" in str(dependencies)
+    assert "pkga/0.1" not in str(dependencies)
     # apply the lockfile to pkgb, should it lock to pkga/0.2
     c.run("graph info pkgb --lockfile=pkgc/conan.lock --format=json")
-    assert "pkga/0.2" in c.stdout
-    assert "pkga/0.1" not in c.stdout
+    dependencies = json.loads(c.stdout)["nodes"]["0"]["dependencies"]
+    assert "pkga/0.2" in str(dependencies)
+    assert "pkga/0.1" not in str(dependencies)
 
 
 @pytest.mark.parametrize("override, force", [(True, False), (False, True)])
@@ -123,14 +125,28 @@ def test_overrides_diamond(override, force):
     assert "pkga/0.2" not in requires
     assert "pkga/0.1" not in requires
     c.run("graph info pkgd --lockfile=pkgd/conan.lock --format=json")
-    assert "pkga/0.3" in c.stdout
-    assert "pkga/0.2" not in c.stdout
-    assert "pkga/0.1" not in c.stdout
+    json_graph = json.loads(c.stdout)
+    deps = json_graph["nodes"]["0"]["dependencies"]
+    assert "pkga/0.3" in str(deps)
+    assert "pkga/0.2" not in str(deps)
+    assert "pkga/0.1" not in str(deps)
+    # Redundant assert, but checking "overrides" summary
+    overrides = json_graph["overrides"]
+    assert len(overrides) == 2
+    assert overrides['pkga/0.1'] == ['pkga/0.3']
+    assert overrides['pkga/0.2'] == ['pkga/0.3#e3afb975277bc245212d6e8de88f4f8f']
+
     # apply the lockfile to pkgb, should it lock to pkga/0.3
     c.run("graph info pkgb --lockfile=pkgd/conan.lock --format=json")
-    assert "pkga/0.3" in c.stdout
-    assert "pkga/0.2" not in c.stdout
-    assert "pkga/0.1" not in c.stdout
+    json_graph = json.loads(c.stdout)
+    deps = json_graph["nodes"]["0"]["dependencies"]
+    assert "pkga/0.3" in str(deps)
+    assert "pkga/0.2" not in str(deps)
+    assert "pkga/0.1" not in str(deps)
+    # Redundant assert, but checking "overrides" summary
+    overrides = json_graph["overrides"]
+    assert len(overrides) == 1
+    assert overrides["pkga/0.1"] == ["pkga/0.3"]
 
 
 @pytest.mark.parametrize("override, force", [(True, False), (False, True)])
@@ -162,14 +178,16 @@ def test_overrides_diamond_ranges(override, force):
     assert "pkga/0.2" not in requires
     assert "pkga/0.1" not in requires
     c.run("graph info pkgd --lockfile=pkgd/conan.lock --format=json")
-    assert "pkga/0.3" in c.stdout
-    assert "pkga/0.2" not in c.stdout
-    assert "pkga/0.1" not in c.stdout
+    dependencies = json.loads(c.stdout)["nodes"]["0"]["dependencies"]
+    assert "pkga/0.3" in str(dependencies)
+    assert "pkga/0.2" not in str(dependencies)
+    assert "pkga/0.1" not in str(dependencies)
     # apply the lockfile to pkgb, should it lock to pkga/0.3
     c.run("graph info pkgb --lockfile=pkgd/conan.lock --format=json")
-    assert "pkga/0.3" in c.stdout
-    assert "pkga/0.2" not in c.stdout
-    assert "pkga/0.1" not in c.stdout
+    dependencies = json.loads(c.stdout)["nodes"]["0"]["dependencies"]
+    assert "pkga/0.3" in str(dependencies)
+    assert "pkga/0.2" not in str(dependencies)
+    assert "pkga/0.1" not in str(dependencies)
 
 
 @pytest.mark.parametrize("override1, force1", [(True, False), (False, True)])
diff --git a/conans/test/integration/package_id/test_validate.py b/conans/test/integration/package_id/test_validate.py
index 3f0f0e32c..13d3ca3a6 100644
--- a/conans/test/integration/package_id/test_validate.py
+++ b/conans/test/integration/package_id/test_validate.py
@@ -44,8 +44,8 @@ class TestValidate(unittest.TestCase):
 
         client.run("graph info --require pkg/0.1 -s os=Windows --format json")
         myjson = json.loads(client.stdout)
-        self.assertEqual(myjson["nodes"][1]["binary"], BINARY_INVALID)
-        assert myjson["nodes"][1]["info_invalid"] == "Windows not supported" in client.out
+        self.assertEqual(myjson["nodes"]["1"]["binary"], BINARY_INVALID)
+        assert myjson["nodes"]["1"]["info_invalid"] == "Windows not supported" in client.out
 
     def test_validate_header_only(self):
         client = TestClient()
diff --git a/conans/test/unittests/tools/cmake/test_cmake_presets_definitions.py b/conans/test/unittests/tools/cmake/test_cmake_presets_definitions.py
index c4f517518..4c07830d1 100644
--- a/conans/test/unittests/tools/cmake/test_cmake_presets_definitions.py
+++ b/conans/test/unittests/tools/cmake/test_cmake_presets_definitions.py
@@ -30,6 +30,17 @@ def conanfile():
     return c
 
 
+def test_cmake_cmake_program(conanfile):
+    mycmake = "C:\\mycmake.exe"
+    conanfile.conf.define("tools.cmake:cmake_program", mycmake)
+
+    with mock.patch("platform.system", mock.MagicMock(return_value='Windows')):
+        write_cmake_presets(conanfile, "the_toolchain.cmake", "MinGW Makefiles", {})
+
+    cmake = CMake(conanfile)
+    assert cmake._cmake_program == mycmake
+
+
 def test_cmake_make_program(conanfile):
     def run(command):
         assert '-DCMAKE_MAKE_PROGRAM="C:/mymake.exe"' in command
@@ -42,4 +53,3 @@ def test_cmake_make_program(conanfile):
 
     cmake = CMake(conanfile)
     cmake.configure()
-