Repository: conan-io/conan
Pull request #18100 (2025-04-08 15:30:31)

fix missing ARM64EC platform in MSBuild
Changelog: Fix: Add ``ARM64EC`` platform in ``MSBuild``, it was missing.
Docs: https://github.com/conan-io/docs/pull/XXXX





Patch:
diff --git a/conan/api/subapi/graph.py b/conan/api/subapi/graph.py
index d3998e9fd..007bae969 100644
--- a/conan/api/subapi/graph.py
+++ b/conan/api/subapi/graph.py
@@ -5,6 +5,7 @@ from conans.client.graph.graph import Node, RECIPE_CONSUMER, CONTEXT_HOST, RECIP
     CONTEXT_BUILD, BINARY_MISSING
 from conans.client.graph.graph_binaries import GraphBinariesAnalyzer
 from conans.client.graph.graph_builder import DepsGraphBuilder
+from conans.client.graph.install_graph import InstallGraph, ProfileArgs
 from conans.client.graph.profile_node_definer import initialize_conanfile_profile, consumer_definer
 from conan.errors import ConanException
 from conan.api.model import RecipeReference
@@ -212,3 +213,27 @@ class GraphAPI:
                     or (missing and ref_matches(node.ref, missing, is_consumer=None))):
                 return node.ref, node.conanfile.info
         raise ConanException("There is no missing binary")
+
+    @staticmethod
+    def build_order(deps_graph, order_by="recipe", reduce=False, profile_args=None):
+        install_graph = InstallGraph(deps_graph, order_by=order_by,
+                                     profile_args=ProfileArgs.from_args(profile_args))
+        if reduce:
+            if order_by is None:
+                raise ConanException("--reduce needs --order-by argument defined")
+            install_graph.reduce()
+        return install_graph
+
+    @staticmethod
+    def build_order_merge(files, reduce=False):
+        result = InstallGraph.load(files[0])
+        if result.reduced:
+            raise ConanException(f"Reduced build-order file cannot be merged: {files[0]}")
+        for f in files[1:]:
+            install_graph = InstallGraph.load(f)
+            if install_graph.reduced:
+                raise ConanException(f"Reduced build-order file cannot be merged: {f}")
+            result.merge(install_graph)
+        if reduce:
+            result.reduce()
+        return result
diff --git a/conan/api/subapi/new.py b/conan/api/subapi/new.py
index addec4c25..494da6e6a 100644
--- a/conan/api/subapi/new.py
+++ b/conan/api/subapi/new.py
@@ -1,10 +1,12 @@
 import fnmatch
 import os
+import shutil
 
 from jinja2 import Template, StrictUndefined, UndefinedError, Environment, meta
 
+from conan.api.output import ConanOutput
 from conan.errors import ConanException
-from conans.util.files import load
+from conans.util.files import load, save
 from conan import __version__
 
 
@@ -14,6 +16,69 @@ class NewAPI:
     def __init__(self, conan_api):
         self.conan_api = conan_api
 
+    def save_template(self, template, defines=None, output_folder=None, force=False):
+        """
+        Save the 'template' files in the output_folder, replacing the template variables
+        with the 'defines'
+        :param template: The name of the template to use
+        :param defines: A list with the 'k=v' variables to replace in the template
+        :param output_folder: The folder where the template files will be saved, cwd if None
+        :param force: If True, overwrite the files if they already exist, otherwise raise an error
+        """
+        # Manually parsing the remainder
+        definitions = {}
+        for u in defines or []:
+            try:
+                k, v = u.split("=", 1)
+            except ValueError:
+                raise ConanException(f"Template definitions must be 'key=value', received {u}")
+            k = k.replace("-", "")  # Remove possible "--name=value"
+            # For variables that only show up once, no need for list to keep compatible behaviour
+            if k in definitions:
+                if isinstance(definitions[k], list):
+                    definitions[k].append(v)
+                else:
+                    definitions[k] = [definitions[k], v]
+            else:
+                definitions[k] = v
+
+        files = self.get_template(template)  # First priority: user folder
+        is_builtin = False
+        if not files:  # then, try the templates in the Conan home
+            files = self.get_home_template(template)
+        if files:
+            template_files, non_template_files = files
+        else:
+            template_files = self.get_builtin_template(template)
+            non_template_files = {}
+            is_builtin = True
+
+        if not template_files and not non_template_files:
+            raise ConanException(f"Template doesn't exist or not a folder: {template}")
+
+        if is_builtin and template == "workspace":  # hardcoded for the workspace special case
+            definitions["name"] = "liba"
+        template_files = self.render(template_files, definitions)
+
+        # Saving the resulting files
+        output = ConanOutput()
+        output_folder = output_folder or os.getcwd()
+        # Making sure they don't overwrite existing files
+        for f, v in sorted(template_files.items()):
+            path = os.path.join(output_folder, f)
+            if os.path.exists(path) and not force:
+                raise ConanException(f"File '{f}' already exists, and --force not defined, aborting")
+            save(path, v)
+            output.success("File saved: %s" % f)
+
+        # copy non-templates
+        for f, v in sorted(non_template_files.items()):
+            path = os.path.join(output_folder, f)
+            if os.path.exists(path) and not force:
+                raise ConanException(f"File '{f}' already exists, and --force not defined, aborting")
+            shutil.copy2(v, path)
+            output.success("File saved: %s" % f)
+
     @staticmethod
     def get_builtin_template(template_name):
         from conan.internal.api.new.basic import basic_file
diff --git a/conan/api/subapi/remotes.py b/conan/api/subapi/remotes.py
index 1bb55664a..65409cc63 100644
--- a/conan/api/subapi/remotes.py
+++ b/conan/api/subapi/remotes.py
@@ -1,6 +1,7 @@
 import fnmatch
 import json
 import os
+from collections import OrderedDict
 from urllib.parse import urlparse
 
 from conan.api.model import Remote, LOCAL_RECIPES_INDEX
@@ -8,6 +9,7 @@ from conan.api.output import ConanOutput
 from conan.internal.cache.home_paths import HomePaths
 from conan.internal.conan_app import ConanBasicApp
 from conans.client.rest.conan_requester import ConanRequester
+from conans.client.rest.remote_credentials import RemoteCredentials
 from conans.client.rest_client_local_recipe_index import add_local_recipes_index_remote, \
     remove_local_recipes_index_remote
 from conan.internal.api.remotes.localdb import LocalDB
@@ -29,14 +31,14 @@ class RemotesAPI:
 
     def __init__(self, conan_api):
         # This method is private, the subapi is not instantiated by users
-        self.conan_api = conan_api
+        self._conan_api = conan_api
         self._home_folder = conan_api.home_folder
         self._remotes_file = HomePaths(self._home_folder).remotes_path
         # Wraps an http_requester to inject proxies, certs, etc
-        self._requester = ConanRequester(self.conan_api.config.global_conf, self.conan_api.cache_folder)
+        self._requester = ConanRequester(self._conan_api.config.global_conf, self._conan_api.cache_folder)
 
     def reinit(self):
-        self._requester = ConanRequester(self.conan_api.config.global_conf, self.conan_api.cache_folder)
+        self._requester = ConanRequester(self._conan_api.config.global_conf, self._conan_api.cache_folder)
 
     def list(self, pattern=None, only_enabled=True):
         """
@@ -224,9 +226,29 @@ class RemotesAPI:
         :param username: the user login as ``str``
         :param password: password ``str``
         """
-        app = ConanBasicApp(self.conan_api)
+        app = ConanBasicApp(self._conan_api)
         app.remote_manager.authenticate(remote, username, password)
 
+    def login(self, remotes, username=None, password=None):
+        creds = RemoteCredentials(self._conan_api.cache_folder, self._conan_api.config.global_conf)
+
+        ret = OrderedDict()
+        for r in remotes:
+            previous_info = self.user_info(r)
+
+            if username is not None and password is not None:
+                user, password = username, password
+            else:
+                user, password, _ = creds.auth(r, username)
+                if username is not None and username != user:
+                    raise ConanException(f"User '{username}' doesn't match user '{user}' in "
+                                         f"credentials.json or environment variables")
+
+            self.user_login(r, user, password)
+            info = self.user_info(r)
+            ret[r.name] = {"previous_info": previous_info, "info": info}
+        return ret
+
     def user_logout(self, remote: Remote):
         """
         Logout from the given :ref:`Remote <conan.api.model.Remote>`
@@ -247,7 +269,7 @@ class RemotesAPI:
     def user_auth(self, remote: Remote, with_user=False, force=False):
         # TODO: Review
         localdb = LocalDB(self._home_folder)
-        app = ConanBasicApp(self.conan_api)
+        app = ConanBasicApp(self._conan_api)
         if with_user:
             user, token, _ = localdb.get_login(remote.url)
             if not user:
diff --git a/conan/cli/commands/graph.py b/conan/cli/commands/graph.py
index 40b27cc1b..290bac5bd 100644
--- a/conan/cli/commands/graph.py
+++ b/conan/cli/commands/graph.py
@@ -12,7 +12,6 @@ from conan.cli.formatters.graph.graph_info_text import format_graph_info
 from conan.cli.printers import print_profiles
 from conan.cli.printers.graph import print_graph_packages, print_graph_basic
 from conan.errors import ConanException
-from conans.client.graph.install_graph import InstallGraph, ProfileArgs
 
 
 def explain_formatter_text(data):
@@ -111,13 +110,8 @@ def graph_build_order(conan_api, parser, subparser, *args):
 
     out = ConanOutput()
     out.title("Computing the build order")
-
-    install_graph = InstallGraph(deps_graph, order_by=args.order_by,
-                                 profile_args=ProfileArgs.from_args(args))
-    if args.reduce:
-        if args.order_by is None:
-            raise ConanException("--reduce needs --order-by argument defined")
-        install_graph.reduce()
+    install_graph = conan_api.graph.build_order(deps_graph, args.order_by, args.reduce,
+                                                profile_args=args)
     install_order_serialized = install_graph.install_build_order()
     if args.order_by is None:  # legacy
         install_order_serialized = install_order_serialized["order"]
@@ -144,21 +138,12 @@ def graph_build_order_merge(conan_api, parser, subparser, *args):  # noqa
     if not args.file or len(args.file) < 2:
         raise ConanException("At least 2 files are needed to be merged")
 
-    result = InstallGraph.load(make_abs_path(args.file[0]))
-    if result.reduced:
-        raise ConanException(f"Reduced build-order file cannot be merged: {args.file[0]}")
-    for f in args.file[1:]:
-        install_graph = InstallGraph.load(make_abs_path(f))
-        if install_graph.reduced:
-            raise ConanException(f"Reduced build-order file cannot be merged: {f}")
-        result.merge(install_graph)
-
-    if args.reduce:
-        result.reduce()
+    files = [make_abs_path(f) for f in args.file]
+    result = conan_api.graph.build_order_merge(files, args.reduce)
+
     install_order_serialized = result.install_build_order()
     if getattr(result, "legacy"):
         install_order_serialized = install_order_serialized["order"]
-
     return {"build_order": install_order_serialized,
             "conan_error": result.get_errors()}
 
@@ -236,13 +221,22 @@ def graph_info(conan_api, parser, subparser, *args):
             base_folder = args.deployer_folder or os.getcwd()
             conan_api.install.deploy(deps_graph, args.deployer, None, base_folder)
 
+    warn_msg = None
+    missing = set(str(n.ref.name) for n in deps_graph.nodes if n.binary == "Missing")
+    invalid = set(str(n.ref.name) for n in deps_graph.nodes if n.binary == "Invalid")
+    if missing or invalid:
+        warn_msg = "There are some error(s) in the graph:"
+        if missing:
+            warn_msg += f"\n    - Missing packages: {', '.join(missing)}"
+        if invalid:
+            warn_msg += f"\n    - Invalid packages: {', '.join(invalid)}"
+
     return {"graph": deps_graph,
             "field_filter": args.filter,
             "package_filter": args.package_filter,
             "conan_api": conan_api,
             "conan_error": str(deps_graph.error) if deps_graph.error else None,
-            # Do not compute graph errors if there are dependency errors
-            "conan_warning": InstallGraph(deps_graph).get_errors() if not deps_graph.error else None}
+            "conan_warning": warn_msg}
 
 
 @conan_subcommand(formatters={"text": explain_formatter_text,
diff --git a/conan/cli/commands/list.py b/conan/cli/commands/list.py
index addfb6848..6b5100b2a 100644
--- a/conan/cli/commands/list.py
+++ b/conan/cli/commands/list.py
@@ -1,4 +1,5 @@
 import json
+import datetime
 
 from conan.api.conan_api import ConanAPI
 from conan.api.model import ListPattern, MultiPackagesList
@@ -7,7 +8,6 @@ from conan.cli import make_abs_path
 from conan.cli.command import conan_command, OnceArgument
 from conan.cli.formatters.list import list_packages_html
 from conan.errors import ConanException
-from conans.util.dates import timestamp_to_str
 
 # Keep them so we don't break other commands that import them, but TODO: Remove later
 remote_color = Color.BRIGHT_BLUE
@@ -19,6 +19,12 @@ field_color = Color.BRIGHT_YELLOW
 value_color = Color.CYAN
 
 
+def _format_timestamp_human(timestamp):
+    # used by ref.repr_humantime() to print human readable time
+    return datetime.datetime.fromtimestamp(int(timestamp),
+                                           datetime.timezone.utc).strftime('%Y-%m-%d %H:%M:%S UTC')
+
+
 def print_serial(item, indent=None, color_index=None):
     indent = "" if indent is None else (indent + "  ")
     color_index = 0 if color_index is None else (color_index + 1)
@@ -78,7 +84,7 @@ def print_list_text(results):
             for k, v in item.items():
                 if isinstance(v, dict) and v.get("timestamp"):
                     timestamp = v.pop("timestamp")
-                    k = f"{k} ({timestamp_to_str(timestamp)})"
+                    k = f"{k} ({_format_timestamp_human(timestamp)})"
                 result[k] = format_timestamps(v)
             return result
         return item
@@ -108,7 +114,7 @@ def prepare_pkglist_compact(pkglist):
             new_rrev = f"{ref}#{rrev}"
             timestamp = rrev_info.pop("timestamp", None)
             if timestamp:
-                new_rrev += f"%{timestamp} ({timestamp_to_str(timestamp)})"
+                new_rrev += f"%{timestamp} ({_format_timestamp_human(timestamp)})"
 
             packages = rrev_info.pop("packages", None)
             if packages:
diff --git a/conan/cli/commands/new.py b/conan/cli/commands/new.py
index a4ea59b87..2e46fe589 100644
--- a/conan/cli/commands/new.py
+++ b/conan/cli/commands/new.py
@@ -1,10 +1,6 @@
 import os
-import shutil
 
-from conan.api.output import ConanOutput
 from conan.cli.command import conan_command
-from conan.errors import ConanException
-from conans.util.files import save
 
 
 @conan_command(group="Creator")
@@ -23,61 +19,10 @@ def new(conan_api, parser, *args):
                         )
     parser.add_argument("-d", "--define", action="append",
                         help="Define a template argument as key=value, e.g., -d name=mypkg")
-    parser.add_argument("-f", "--force", action='store_true', help="Overwrite file if it already exists")
+    parser.add_argument("-f", "--force", action='store_true',
+                        help="Overwrite file if it already exists")
     parser.add_argument("-o", "--output", help="Output folder for the generated files",
                         default=os.getcwd())
 
     args = parser.parse_args(*args)
-    # Manually parsing the remainder
-    definitions = {}
-    for u in args.define or []:
-        try:
-            k, v = u.split("=", 1)
-        except ValueError:
-            raise ConanException(f"Template definitions must be 'key=value', received {u}")
-        k = k.replace("-", "")  # Remove possible "--name=value"
-        # For variables that only show up once, no need for list to keep compatible behaviour
-        if k in definitions:
-            if isinstance(definitions[k], list):
-                definitions[k].append(v)
-            else:
-                definitions[k] = [definitions[k], v]
-        else:
-            definitions[k] = v
-
-    files = conan_api.new.get_template(args.template)  # First priority: user folder
-    is_builtin = False
-    if not files:  # then, try the templates in the Conan home
-        files = conan_api.new.get_home_template(args.template)
-    if files:
-        template_files, non_template_files = files
-    else:
-        template_files = conan_api.new.get_builtin_template(args.template)
-        non_template_files = {}
-        is_builtin = True
-
-    if not template_files and not non_template_files:
-        raise ConanException("Template doesn't exist or not a folder: {}".format(args.template))
-
-    if is_builtin and args.template == "workspace":  # hardcoded for the workspace special case
-        definitions["name"] = "liba"
-    template_files = conan_api.new.render(template_files, definitions)
-
-    # Saving the resulting files
-    output = ConanOutput()
-    output_folder = args.output
-    # Making sure they don't overwrite existing files
-    for f, v in sorted(template_files.items()):
-        path = os.path.join(output_folder, f)
-        if os.path.exists(path) and not args.force:
-            raise ConanException(f"File '{f}' already exists, and --force not defined, aborting")
-        save(path, v)
-        output.success("File saved: %s" % f)
-
-    # copy non-templates
-    for f, v in sorted(non_template_files.items()):
-        path = os.path.join(output_folder, f)
-        if os.path.exists(path) and not args.force:
-            raise ConanException(f"File '{f}' already exists, and --force not defined, aborting")
-        shutil.copy2(v, path)
-        output.success("File saved: %s" % f)
+    conan_api.new.save_template(args.template, args.define, args.output, args.force)
diff --git a/conan/cli/commands/profile.py b/conan/cli/commands/profile.py
index 16457a164..68c32b943 100644
--- a/conan/cli/commands/profile.py
+++ b/conan/cli/commands/profile.py
@@ -6,7 +6,6 @@ from conan.cli.command import conan_command, conan_subcommand
 from conan.cli.formatters import default_json_formatter
 from conan.cli.args import add_profiles_args
 from conan.errors import ConanException
-from conans.util.files import save
 
 
 def _print_profiles(profiles):
@@ -92,11 +91,15 @@ def profile_detect(conan_api, parser, subparser, *args):
                           "change in future Conan versions.")
     ConanOutput().warning("Use your own profile files for stability.")
     ConanOutput().success(f"Saving detected profile to {profile_pathname}")
-    save(profile_pathname, contents)
+    dir_path = os.path.dirname(profile_pathname)
+    if dir_path:
+        os.makedirs(dir_path, exist_ok=True)
+    with open(profile_pathname, "w", encoding="utf-8", newline="") as f:
+        f.write(contents)
 
 
 @conan_subcommand(formatters={"text": profiles_list_cli_output, "json": default_json_formatter})
-def profile_list(conan_api, parser, subparser, *args):
+def profile_list(conan_api, parser, subparser, *args):  # noqa
     """
     List all profiles in the cache.
     """
@@ -105,7 +108,7 @@ def profile_list(conan_api, parser, subparser, *args):
 
 
 @conan_command(group="Consumer")
-def profile(conan_api, parser, *args):
+def profile(conan_api, parser, *args):  # noqa
     """
     Manage profiles.
     """
diff --git a/conan/cli/commands/remote.py b/conan/cli/commands/remote.py
index a22529f54..83eec7f5c 100644
--- a/conan/cli/commands/remote.py
+++ b/conan/cli/commands/remote.py
@@ -10,22 +10,21 @@ from conan.cli.command import conan_command, conan_subcommand, OnceArgument
 from conan.cli.commands.list import remote_color, error_color, recipe_color, \
     reference_color
 from conan.errors import ConanException
-from conans.client.rest.remote_credentials import RemoteCredentials
 
 
-def formatter_remote_list_json(remotes):
+def _print_remotes_json(remotes):
     info = [{"name": r.name, "url": r.url, "verify_ssl": r.verify_ssl, "enabled": not r.disabled}
             for r in remotes]
     cli_out_write(json.dumps(info, indent=4))
 
 
-def print_remote_list(remotes):
+def _print_remote_list(remotes):
     for r in remotes:
         output_str = str(r)
         cli_out_write(output_str)
 
 
-def print_remote_user_list(results):
+def _print_remote_user_list(results):
     for remote_name, result in results.items():
         cli_out_write(f"{remote_name}:", fg=remote_color)
         if result["user_name"] is None:
@@ -37,7 +36,7 @@ def print_remote_user_list(results):
             cli_out_write(result["authenticated"], fg=reference_color)
 
 
-def print_remote_user_set(results):
+def _print_remote_user_set(results):
     for remote_name, result in results.items():
         from_user = "'{}'".format(result["previous_info"]["user_name"])
         from_user += " (anonymous)" \
@@ -49,12 +48,12 @@ def print_remote_user_set(results):
         cli_out_write(message)
 
 
-def output_remotes_json(results):
+def _print_remotes_users_json(results):
     cli_out_write(json.dumps(list(results.values())))
 
 
-@conan_subcommand(formatters={"text": print_remote_list, "json": formatter_remote_list_json})
-def remote_list(conan_api: ConanAPI, parser, subparser, *args):
+@conan_subcommand(formatters={"text": _print_remote_list, "json": _print_remotes_json})
+def remote_list(conan_api: ConanAPI, parser, subparser, *args):  # noqa
     """
     List current remotes.
     """
@@ -92,7 +91,7 @@ def remote_add(conan_api, parser, subparser, *args):
     conan_api.remotes.add(r, force=args.force, index=args.index)
 
 
-@conan_subcommand(formatters={"text": print_remote_list})
+@conan_subcommand(formatters={"text": _print_remote_list})
 def remote_remove(conan_api, parser, subparser, *args):
     """
     Remove remotes.
@@ -138,7 +137,7 @@ def remote_rename(conan_api, parser, subparser, *args):
     conan_api.remotes.rename(args.remote, args.new_name)
 
 
-@conan_subcommand(formatters={"text": print_remote_list, "json": formatter_remote_list_json})
+@conan_subcommand(formatters={"text": _print_remote_list, "json": _print_remotes_json})
 def remote_enable(conan_api, parser, subparser, *args):
     """
     Enable all the remotes matching a pattern.
@@ -149,7 +148,7 @@ def remote_enable(conan_api, parser, subparser, *args):
     return conan_api.remotes.enable(args.remote)
 
 
-@conan_subcommand(formatters={"text": print_remote_list, "json": formatter_remote_list_json})
+@conan_subcommand(formatters={"text": _print_remote_list, "json": _print_remotes_json})
 def remote_disable(conan_api, parser, subparser, *args):
     """
     Disable all the remotes matching a pattern.
@@ -162,8 +161,8 @@ def remote_disable(conan_api, parser, subparser, *args):
 
 # ### User related commands
 
-@conan_subcommand(formatters={"text": print_remote_user_list, "json": output_remotes_json})
-def remote_list_users(conan_api, parser, subparser, *args):
+@conan_subcommand(formatters={"text": _print_remote_user_list, "json": _print_remotes_users_json})
+def remote_list_users(conan_api, parser, subparser, *args):  # noqa
     """
     List the users logged into all the remotes.
     """
@@ -177,7 +176,7 @@ def remote_list_users(conan_api, parser, subparser, *args):
     return ret
 
 
-@conan_subcommand(formatters={"text": print_remote_user_set, "json": output_remotes_json})
+@conan_subcommand(formatters={"text": _print_remote_user_set, "json": _print_remotes_users_json})
 def remote_login(conan_api, parser, subparser, *args):
     """
     Login into the specified remotes matching a pattern.
@@ -194,29 +193,10 @@ def remote_login(conan_api, parser, subparser, *args):
     remotes = conan_api.remotes.list(pattern=args.remote, only_enabled=False)
     if not remotes:
         raise ConanException("There are no remotes matching the '{}' pattern".format(args.remote))
+    return conan_api.remotes.login(remotes, args.username, args.password)
 
-    creds = RemoteCredentials(conan_api.cache_folder, conan_api.config.global_conf)
 
-    ret = OrderedDict()
-    for r in remotes:
-        previous_info = conan_api.remotes.user_info(r)
-
-        if args.username is not None and args.password is not None:
-            user, password = args.username, args.password
-        else:
-            user, password, _ = creds.auth(r, args.username)
-            if args.username is not None and args.username != user:
-                raise ConanException(f"User '{args.username}' doesn't match user '{user}' in "
-                                     f"credentials.json or environment variables")
-
-        conan_api.remotes.user_login(r, user, password)
-        info = conan_api.remotes.user_info(r)
-        ret[r.name] = {"previous_info": previous_info, "info": info}
-
-    return ret
-
-
-@conan_subcommand(formatters={"text": print_remote_user_set, "json": output_remotes_json})
+@conan_subcommand(formatters={"text": _print_remote_user_set, "json": _print_remotes_users_json})
 def remote_set_user(conan_api, parser, subparser, *args):
     """
     Associate a username with a remote matching a pattern without performing the authentication.
@@ -240,7 +220,7 @@ def remote_set_user(conan_api, parser, subparser, *args):
     return ret
 
 
-@conan_subcommand(formatters={"text": print_remote_user_set, "json": output_remotes_json})
+@conan_subcommand(formatters={"text": _print_remote_user_set, "json": _print_remotes_users_json})
 def remote_logout(conan_api, parser, subparser, *args):
     """
     Clear the existing credentials for the specified remotes matching a pattern.
@@ -261,7 +241,7 @@ def remote_logout(conan_api, parser, subparser, *args):
     return ret
 
 
-def print_auth(remotes):
+def _print_auth(remotes):
     for remote_name, msg in remotes.items():
         if msg is None:
             cli_out_write(f"{remote_name}: No user defined")
@@ -271,11 +251,11 @@ def print_auth(remotes):
                 cli_out_write(f"    {k}: {v}", fg=Color.BRIGHT_RED if k == "error" else Color.WHITE)
 
 
-def print_auth_json(results):
+def _print_auth_json(results):
     cli_out_write(json.dumps(results))
 
 
-@conan_subcommand(formatters={"text": print_auth, "json": print_auth_json})
+@conan_subcommand(formatters={"text": _print_auth, "json": _print_auth_json})
 def remote_auth(conan_api, parser, subparser, *args):
     """
     Authenticate in the defined remotes. Use CONAN_LOGIN_USERNAME* and CONAN_PASSWORD* variables if available.
@@ -310,7 +290,7 @@ def remote_auth(conan_api, parser, subparser, *args):
 
 
 @conan_command(group="Consumer")
-def remote(conan_api, parser, *args):
+def remote(conan_api, parser, *args):  # noqa
     """
     Manage the remote list and the users authenticated on them.
     """
diff --git a/conan/cli/formatters/graph/graph.py b/conan/cli/formatters/graph/graph.py
index 06a51d923..70063d0a4 100644
--- a/conan/cli/formatters/graph/graph.py
+++ b/conan/cli/formatters/graph/graph.py
@@ -7,14 +7,13 @@ from conan.api.output import cli_out_write
 from conan.cli.formatters.graph.graph_info_text import filter_graph
 from conan.cli.formatters.graph.info_graph_dot import graph_info_dot
 from conan.cli.formatters.graph.info_graph_html import graph_info_html
-from conans.client.graph.graph_error import GraphConflictError
 
 
-def _render_graph(graph, error, template, template_folder):
+def _render_graph(graph, template, template_folder):
     deps_graph = graph.serialize()
     from conan import __version__
     template = Template(template, autoescape=select_autoescape(['html', 'xml']))
-    return template.render(deps_graph=deps_graph, error=error,
+    return template.render(deps_graph=deps_graph,
                            base_template_path=template_folder, version=__version__)
 
 
@@ -28,15 +27,7 @@ def format_graph_html(result):
     if os.path.isfile(user_template):
         with open(user_template, 'r', encoding="utf-8", newline="") as handle:
             template = handle.read()
-    error = {
-        "type": "unknown",
-        "context": graph.error,
-        "should_highlight_node": lambda node: False
-    }
-    if isinstance(graph.error, GraphConflictError):
-        error["type"] = "conflict"
-        error["should_highlight_node"] = lambda node: node.id == graph.error.node.id
-    cli_out_write(_render_graph(graph, error, template, template_folder))
+    cli_out_write(_render_graph(graph, template, template_folder))
 
 
 def format_graph_dot(result):
@@ -49,7 +40,7 @@ def format_graph_dot(result):
     if os.path.isfile(user_template):
         with open(user_template, 'r', encoding="utf-8", newline="") as handle:
             template = handle.read()
-    cli_out_write(_render_graph(graph, None, template, template_folder))
+    cli_out_write(_render_graph(graph, template, template_folder))
 
 
 def format_graph_json(result):
diff --git a/conan/cli/printers/graph.py b/conan/cli/printers/graph.py
index 84c671f4a..d860ad3b8 100644
--- a/conan/cli/printers/graph.py
+++ b/conan/cli/printers/graph.py
@@ -1,7 +1,4 @@
 from conan.api.output import ConanOutput, Color, LEVEL_VERBOSE, LEVEL_DEBUG
-from conans.client.graph.graph import BINARY_INVALID, BINARY_MISSING, RECIPE_CONSUMER, \
-    RECIPE_VIRTUAL, CONTEXT_BUILD, BINARY_SKIP, \
-    BINARY_PLATFORM, BINARY_BUILD
 
 
 def print_graph_basic(graph):
@@ -20,9 +17,9 @@ def print_graph_basic(graph):
         if hasattr(node.conanfile, "python_requires"):
             for _, r in node.conanfile.python_requires.items():
                 python_requires[r.ref] = r.recipe, r.remote
-        if node.recipe in (RECIPE_CONSUMER, RECIPE_VIRTUAL):
+        if node.recipe in ("Consumer", "Cli"):
             continue
-        if node.context == CONTEXT_BUILD:
+        if node.context == "build":
             build_requires[node.ref] = node.recipe, node.remote
         else:
             if node.test:
@@ -113,18 +110,21 @@ def print_graph_packages(graph):
     skipped_requires = []
     tab = "    "
     for node in graph.nodes:
-        if node.recipe in (RECIPE_CONSUMER, RECIPE_VIRTUAL):
+        if node.recipe in ("Consumer", "Cli"):
             continue
         node_info = node.conanfile.info
-        if node.context == CONTEXT_BUILD:
-            existing = build_requires.setdefault(node.pref, [node.binary, node.binary_remote, node_info])
+        if node.context == "build":
+            existing = build_requires.setdefault(node.pref,
+                                                 [node.binary, node.binary_remote, node_info])
         else:
             if node.test:
-                existing = test_requires.setdefault(node.pref, [node.binary, node.binary_remote, node_info])
+                existing = test_requires.setdefault(node.pref,
+                                                    [node.binary, node.binary_remote, node_info])
             else:
-                existing = requires.setdefault(node.pref, [node.binary, node.binary_remote, node_info])
+                existing = requires.setdefault(node.pref,
+                                               [node.binary, node.binary_remote, node_info])
         # TO avoid showing as "skip" something that is used in other node of the graph
-        if existing[0] == BINARY_SKIP:
+        if existing[0] == "Skip":
             existing[0] = node.binary
 
     def _format_requires(title, reqs_to_print):
@@ -132,15 +132,15 @@ def print_graph_packages(graph):
             return
         output.info(title, Color.BRIGHT_YELLOW)
         for pref, (status, remote, info) in sorted(reqs_to_print.items(), key=repr):
-            name = pref.repr_notime() if status != BINARY_PLATFORM else str(pref.ref)
+            name = pref.repr_notime() if status != "Platform" else str(pref.ref)
             msg = f"{tab}{name} - "
-            if status == BINARY_SKIP:
+            if status == "Skip":
                 skipped_requires.append(str(pref.ref))
                 output.verbose(f"{msg}{status}", Color.BRIGHT_CYAN)
-            elif status == BINARY_MISSING or status == BINARY_INVALID:
+            elif status == "Missing" or status == "Invalid":
                 output.write(msg, Color.BRIGHT_CYAN)
                 output.writeln(status, Color.BRIGHT_RED)
-            elif status == BINARY_BUILD:
+            elif status == "Build":
                 output.write(msg, Color.BRIGHT_CYAN)
                 output.writeln(status, Color.BRIGHT_YELLOW)
             else:
diff --git a/conan/internal/model/info.py b/conan/internal/model/info.py
index 231b52551..2cadf1e8d 100644
--- a/conan/internal/model/info.py
+++ b/conan/internal/model/info.py
@@ -441,8 +441,6 @@ class ConanInfo:
                 result.append(f"{key}: " + " ".join(f"{k}={v}" for k, v in values.items()))
             elif isinstance(values, type([])):
                 result.append(f"{key}: {' '.join(values)}")
-            elif values:
-                result.append(f"{key}: {' '.join(values)}")
 
         return result
 
diff --git a/conan/tools/cmake/toolchain/blocks.py b/conan/tools/cmake/toolchain/blocks.py
index 5372c8560..694f3a364 100644
--- a/conan/tools/cmake/toolchain/blocks.py
+++ b/conan/tools/cmake/toolchain/blocks.py
@@ -15,7 +15,7 @@ from conan.tools.build.cross_building import cross_building
 from conan.tools.cmake.toolchain import CONAN_TOOLCHAIN_FILENAME
 from conan.tools.cmake.utils import is_multi_configuration
 from conan.tools.intel import IntelCC
-from conan.tools.microsoft.visual import msvc_version_to_toolset_version
+from conan.tools.microsoft.visual import msvc_version_to_toolset_version, msvc_platform_from_arch
 from conan.internal.api.install.generators import relativize_path
 from conans.client.subsystems import deduce_subsystem, WINDOWS
 from conan.errors import ConanException
@@ -991,11 +991,7 @@ class GenericSystemBlock(Block):
             return settings.get_safe("os.platform")
 
         if compiler in ("msvc", "clang") and generator and "Visual" in generator:
-            return {"x86": "Win32",
-                    "x86_64": "x64",
-                    "armv7": "ARM",
-                    "armv8": "ARM64",
-                    "arm64ec": "ARM64EC"}.get(arch)
+            return msvc_platform_from_arch(arch)
         return None
 
     def _get_generic_system_name(self):
diff --git a/conan/tools/microsoft/layout.py b/conan/tools/microsoft/layout.py
index 6a4c73133..006d5b2c1 100644
--- a/conan/tools/microsoft/layout.py
+++ b/conan/tools/microsoft/layout.py
@@ -1,7 +1,7 @@
 import os
 
-from conan.tools.microsoft.msbuild import msbuild_arch
 from conan.errors import ConanException
+from conan.tools.microsoft.visual import msvc_platform_from_arch
 
 
 def vs_layout(conanfile):
@@ -26,7 +26,7 @@ def vs_layout(conanfile):
         raise ConanException("The 'vs_layout' requires the 'arch' setting")
 
     if arch != "None" and arch != "x86":
-        msvc_arch = msbuild_arch(arch)
+        msvc_arch = msvc_platform_from_arch(arch)
         if not msvc_arch:
             raise ConanException(f"The 'vs_layout' doesn't work with the arch '{arch}'. "
                                  "Accepted architectures: 'x86', 'x86_64', 'armv7', 'armv8'")
diff --git a/conan/tools/microsoft/msbuild.py b/conan/tools/microsoft/msbuild.py
index e8efb2772..38a3ae6a8 100644
--- a/conan/tools/microsoft/msbuild.py
+++ b/conan/tools/microsoft/msbuild.py
@@ -1,4 +1,5 @@
 from conan.errors import ConanException
+from conan.tools.microsoft.visual import msvc_platform_from_arch
 
 
 def msbuild_verbosity_cmd_line_arg(conanfile):
@@ -17,14 +18,7 @@ def msbuild_verbosity_cmd_line_arg(conanfile):
     return ""
 
 
-def msbuild_arch(arch):
-    return {'x86': 'x86',
-            'x86_64': 'x64',
-            'armv7': 'ARM',
-            'armv8': 'ARM64'}.get(str(arch))
-
-
-class MSBuild(object):
+class MSBuild:
     """
     MSBuild build helper class
     """
@@ -39,11 +33,12 @@ class MSBuild(object):
         # if platforms:
         #    msvc_arch.update(platforms)
         arch = conanfile.settings.get_safe("arch")
-        msvc_arch = msbuild_arch(arch)
+        # MSVC default platform for VS projects is "x86", not "Win32" (but CMake default is "Win32")
+        msvc_platform = msvc_platform_from_arch(arch) if arch != "x86" else "x86"
         if conanfile.settings.get_safe("os") == "WindowsCE":
-            msvc_arch = conanfile.settings.get_safe("os.platform")
+            msvc_platform = conanfile.settings.get_safe("os.platform")
         #: Defines the platform name, e.g., ``ARM`` if ``settings.arch == "armv7"``.
-        self.platform = msvc_arch
+        self.platform = msvc_platform
 
     def command(self, sln, targets=None):
         """
diff --git a/conan/tools/microsoft/msbuilddeps.py b/conan/tools/microsoft/msbuilddeps.py
index 258a56b97..4ea1b7345 100644
--- a/conan/tools/microsoft/msbuilddeps.py
+++ b/conan/tools/microsoft/msbuilddeps.py
@@ -10,6 +10,7 @@ from conan.internal import check_duplicated_generator
 from conan.errors import ConanException
 from conan.internal.api.install.generators import relativize_path
 from conan.internal.model.dependencies import get_transitive_requires
+from conan.tools.microsoft.visual import msvc_platform_from_arch
 from conans.util.files import load, save
 
 VALID_LIB_EXTENSIONS = (".so", ".lib", ".a", ".dylib", ".bc")
@@ -102,10 +103,7 @@ class MSBuildDeps:
         # TODO: This platform is not exactly the same as ``msbuild_arch``, because it differs
         # in x86=>Win32
         #: Platform name, e.g., ``Win32`` if ``settings.arch == "x86"``.
-        self.platform = {'x86': 'Win32',
-                         'x86_64': 'x64',
-                         'armv7': 'ARM',
-                         'armv8': 'ARM64'}.get(str(conanfile.settings.arch))
+        self.platform = msvc_platform_from_arch(str(conanfile.settings.arch))
         #: Defines the platform key used to conditionally select which property sheet to
         #: import (defaults to ``"Platform"``).
         self.platform_key = "Platform"
diff --git a/conan/tools/microsoft/toolchain.py b/conan/tools/microsoft/toolchain.py
index f7fb6214c..74a84bfe1 100644
--- a/conan/tools/microsoft/toolchain.py
+++ b/conan/tools/microsoft/toolchain.py
@@ -7,7 +7,8 @@ from jinja2 import Template
 from conan.internal import check_duplicated_generator
 from conan.tools.build import build_jobs
 from conan.tools.intel.intel_cc import IntelCC
-from conan.tools.microsoft.visual import VCVars, msvs_toolset, msvc_runtime_flag
+from conan.tools.microsoft.visual import VCVars, msvs_toolset, msvc_runtime_flag, \
+    msvc_platform_from_arch
 from conan.errors import ConanException
 from conans.util.files import save, load
 
@@ -77,13 +78,9 @@ class MSBuildToolchain(object):
         self.properties = {}
 
     def _name_condition(self, settings):
+        platform = msvc_platform_from_arch(settings.get_safe("arch"))
         props = [("Configuration", self.configuration),
-                 # TODO: refactor, put in common with MSBuildDeps. Beware this is != msbuild_arch
-                 #  because of Win32
-                 ("Platform", {'x86': 'Win32',
-                               'x86_64': 'x64',
-                               'armv7': 'ARM',
-                               'armv8': 'ARM64'}.get(settings.get_safe("arch")))]
+                 ("Platform", platform)]
 
         name = "".join("_%s" % v for _, v in props if v is not None)
         condition = " And ".join("'$(%s)' == '%s'" % (k, v) for k, v in props if v is not None)
diff --git a/conan/tools/microsoft/visual.py b/conan/tools/microsoft/visual.py
index 3a0c9e3b5..507c7680b 100644
--- a/conan/tools/microsoft/visual.py
+++ b/conan/tools/microsoft/visual.py
@@ -12,6 +12,14 @@ from conans.util.files import save
 CONAN_VCVARS = "conanvcvars"
 
 
+def msvc_platform_from_arch(arch):
+    return {"x86": "Win32",
+            "x86_64": "x64",
+            "armv7": "ARM",
+            "armv8": "ARM64",
+            "arm64ec": "ARM64EC"}.get(arch)
+
+
 def check_min_vs(conanfile, version, raise_invalid=True):
     """
     This is a helper method to allow the migration of 1.X -> 2.0 and VisualStudio -> msvc settings
diff --git a/conan/tools/sbom/__init__.py b/conan/tools/sbom/__init__.py
index 1f1641e12..8d8cd91e2 100644
--- a/conan/tools/sbom/__init__.py
+++ b/conan/tools/sbom/__init__.py
@@ -1,1 +1,1 @@
-from conan.tools.sbom.cyclonedx import cyclonedx_1_4
+from conan.tools.sbom.cyclonedx import cyclonedx_1_4, cyclonedx_1_6
diff --git a/conan/tools/sbom/cyclonedx.py b/conan/tools/sbom/cyclonedx.py
index bca937a33..e24aca912 100644
--- a/conan/tools/sbom/cyclonedx.py
+++ b/conan/tools/sbom/cyclonedx.py
@@ -1,4 +1,4 @@
-
+from conan import conan_version
 
 def cyclonedx_1_4(conanfile, name=None, add_build=False, add_tests=False, **kwargs):
     """
@@ -19,7 +19,7 @@ def cyclonedx_1_4(conanfile, name=None, add_build=False, add_tests=False, **kwar
 
     Example usage:
     ```
-    cyclonedx(conanfile, name="custom_name", add_build=True, add_test=True, **kwargs)
+    cyclonedx_1_4(conanfile, name="custom_name", add_build=True, add_test=True, **kwargs)
     ```
 
     """
@@ -73,7 +73,7 @@ def cyclonedx_1_4(conanfile, name=None, add_build=False, add_tests=False, **kwar
             **({"licenses": _calculate_licenses(node)} if node.conanfile.license else {}),
             "name": node.name,
             "purl": f"pkg:conan/{node.name}@{node.ref.version}",
-            "type": "library",
+            "type": "application" if node.conanfile.package_type == "application" else "library",
             "version": str(node.ref.version),
         } for node in nodes]} if nodes else {}),
         **({"dependencies": dependencies} if dependencies else {}),
@@ -82,7 +82,7 @@ def cyclonedx_1_4(conanfile, name=None, add_build=False, add_tests=False, **kwar
                 "author": conanfile.author or "Unknown",
                 "bom-ref": special_id if has_special_root_node else f"pkg:conan/{conanfile.name}@{conanfile.ref.version}?rref={conanfile.ref.revision}",
                 "name": name if name else name_default,
-                "type": "library"
+                "type": "application" if conanfile.package_type == "application" else "library",
             },
             "timestamp": f"{datetime.fromtimestamp(time.time(), tz=timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')}",
             "tools": [{
@@ -99,3 +99,103 @@ def cyclonedx_1_4(conanfile, name=None, add_build=False, add_tests=False, **kwar
         "version": 1,
     }
     return sbom_cyclonedx_1_4
+
+def cyclonedx_1_6(conanfile, name=None, add_build=False, add_tests=False, **kwargs):
+    """
+    (Experimental) Generate cyclone 1.6 SBOM with JSON format
+
+    Creates a CycloneDX 1.6 Software Bill of Materials (SBOM) from a given dependency graph.
+
+
+
+    Parameters:
+        conanfile: The conanfile instance.
+        name (str, optional): Custom name for the metadata field.
+        add_build (bool, optional, default=False): Include build dependencies.
+        add_tests (bool, optional, default=False): Include test dependencies.
+
+    Returns:
+        The generated CycloneDX 1.6 document as a string.
+
+    Example usage:
+    ```
+    cyclonedx_1_6(conanfile, name="custom_name", add_build=True, add_test=True, **kwargs)
+    ```
+
+    """
+    import uuid
+    import time
+    from datetime import datetime, timezone
+    graph = conanfile.subgraph
+
+    has_special_root_node = not (getattr(graph.root.ref, "name", False) and getattr(graph.root.ref, "version", False) and getattr(graph.root.ref, "revision", False))
+    special_id = str(uuid.uuid4())
+
+    name_default = getattr(graph.root.ref, "name", False) or "conan-sbom"
+    name_default += f"/{graph.root.ref.version}" if bool(getattr(graph.root.ref, "version", False)) else ""
+    nodes = [node for node in graph.nodes if (node.context == "host" or add_build) and (not node.test or add_tests)]
+    if has_special_root_node:
+        nodes = nodes[1:]
+
+    dependencies = []
+    if has_special_root_node:
+        deps = {"ref": special_id,
+                "dependsOn": [f"pkg:conan/{d.dst.name}@{d.dst.ref.version}?rref={d.dst.ref.revision}"
+                              for d in graph.root.dependencies]}
+        dependencies.append(deps)
+    for c in nodes:
+        deps = {"ref": f"pkg:conan/{c.name}@{c.ref.version}?rref={c.ref.revision}"}
+        dep = [d for d in c.dependencies if (d.dst.context == "host" or add_build) and (not d.dst.test or add_tests)]
+
+        depends_on = [f"pkg:conan/{d.dst.name}@{d.dst.ref.version}?rref={d.dst.ref.revision}" for d in dep]
+        if depends_on:
+            deps["dependsOn"] = depends_on
+        dependencies.append(deps)
+
+    def _calculate_licenses(component):
+        if isinstance(component.conanfile.license, str): # Just one license
+            return [{"license": {
+                        "id": component.conanfile.license
+                    }}]
+        return [{"license": {
+                    "id": l
+                }} for l in component.conanfile.license]
+
+    sbom_cyclonedx_1_6 = {
+        **({"components": [{
+            **({"authors": [node.conanfile.author]} if node.conanfile.author else {}),
+            "bom-ref": special_id if has_special_root_node else f"pkg:conan/{node.name}@{node.ref.version}?rref={node.ref.revision}",
+            "description": node.conanfile.description,
+            **({"externalReferences": [{
+                "type": "website",
+                "url": node.conanfile.homepage
+            }]} if node.conanfile.homepage else {}),
+            **({"licenses": _calculate_licenses(node)} if node.conanfile.license else {}),
+            "name": node.name,
+            "purl": f"pkg:conan/{node.name}@{node.ref.version}",
+            "type": "application" if node.conanfile.package_type == "application" else "library",
+            "version": str(node.ref.version),
+        } for node in nodes]} if nodes else {}),
+        **({"dependencies": dependencies} if dependencies else {}),
+        "metadata": {
+            "component": {
+                **({"authors": [conanfile.author]} if conanfile.author else {}),
+                "bom-ref": special_id if has_special_root_node else f"pkg:conan/{conanfile.name}@{conanfile.ref.version}?rref={conanfile.ref.revision}",
+                "name": name if name else name_default,
+                "type": "application" if conanfile.package_type == "application" else "library"
+            },
+            "timestamp": f"{datetime.fromtimestamp(time.time(), tz=timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')}",
+            "tools": {
+                "components":[{
+                    "type": "application",
+                    "name": "Conan-io",
+                    "version": str(conan_version),
+                }]
+            },
+        },
+        "serialNumber": f"urn:uuid:{uuid.uuid4()}",
+        "bomFormat": "CycloneDX",
+        "specVersion": "1.6",
+        "version": 1,
+    }
+    return sbom_cyclonedx_1_6
diff --git a/conans/client/graph/install_graph.py b/conans/client/graph/install_graph.py
index 4a0fdaf9e..b68dec9f2 100644
--- a/conans/client/graph/install_graph.py
+++ b/conans/client/graph/install_graph.py
@@ -66,7 +66,7 @@ class _InstallPackageReference:
         self.nodes.append(node)
 
     def _build_args(self):
-        if self.binary != BINARY_BUILD:
+        if self.binary not in (BINARY_BUILD, BINARY_EDITABLE_BUILD):
             return None
         cmd = f"--requires={self.ref}" if self.context == "host" else f"--tool-requires={self.ref}"
         compatible = "compatible:" if self.info and self.info.get("compatibility_delta") else ""
@@ -290,7 +290,7 @@ class _InstallConfiguration:
                     self.depends.append(dep.dst.pref)
 
     def _build_args(self):
-        if self.binary != BINARY_BUILD:
+        if self.binary not in (BINARY_BUILD, BINARY_EDITABLE_BUILD):
             return None
         cmd = f"--requires={self.ref}" if self.context == "host" else f"--tool-requires={self.ref}"
         compatible = "compatible:" if self.info and self.info.get("compatibility_delta") else ""
diff --git a/conans/model/package_ref.py b/conans/model/package_ref.py
index 0669fc435..55fe086f3 100644
--- a/conans/model/package_ref.py
+++ b/conans/model/package_ref.py
@@ -17,7 +17,8 @@ class PkgReference(_PkgReference):
 
     def __init__(self, *args, **kwargs):
         if PkgReference.usage_counter == 0:
-            ConanOutput().warning(_msg.format("from conans.model.package_ref import PkgReference"))
+            ConanOutput().warning(_msg.format("from conans.model.package_ref import PkgReference"),
+                                  warn_tag="deprecated")
         PkgReference.usage_counter += 1
         super(PkgReference, self).__init__(*args, **kwargs)
 


Test patch:
diff --git a/test/functional/sbom/test_cyclonedx.py b/test/functional/sbom/test_cyclonedx.py
index 1352e80be..a7fbf2fa7 100644
--- a/test/functional/sbom/test_cyclonedx.py
+++ b/test/functional/sbom/test_cyclonedx.py
@@ -270,3 +270,67 @@ def test_sbom_generation_custom_name(name, result):
     tc.run("install .")
     assert os.path.exists(os.path.join(tc.current_folder, "sbom", "sbom.cdx.json"))
     assert f'"name": "{result}"' in tc.load(os.path.join(tc.current_folder, "sbom", "sbom.cdx.json"))
+
+@pytest.mark.parametrize("cyclone_version", ["cyclonedx_1_4", "cyclonedx_1_6"])
+def test_cyclonedx_check_content(cyclone_version):
+    _sbom_hook_post_package = textwrap.dedent("""
+    import json
+    import os
+    from conan.errors import ConanException
+    from conan.api.output import ConanOutput
+    from conan.tools.sbom import %s
+
+    def post_package(conanfile):
+        sbom_cyclonedx= %s(conanfile)
+        metadata_folder = conanfile.package_metadata_folder
+        file_name = "sbom.cdx.json"
+        with open(os.path.join(metadata_folder, file_name), 'w') as f:
+            json.dump(sbom_cyclonedx, f, indent=4)
+        ConanOutput().success(f"CYCLONEDX CREATED - {conanfile.package_metadata_folder}")
+    """)
+    tc = TestClient()
+    hook_path = os.path.join(tc.paths.hooks_path, "hook_sbom.py")
+    save(hook_path, _sbom_hook_post_package % (cyclone_version, cyclone_version))
+    conanfile_bar = textwrap.dedent("""
+            from conan import ConanFile
+            class HelloConan(ConanFile):
+                name = 'bar'
+                version = '1.0'
+                author = 'conan-dev'
+                package_type = 'application'
+        """)
+
+    conanfile_foo = textwrap.dedent("""
+        from conan import ConanFile
+        class HelloConan(ConanFile):
+            name = 'foo'
+            version = '1.0'
+            author = 'conan-dev'
+            package_type = 'application'
+
+            def requirements(self):
+                self.requires("bar/1.0")
+    """)
+    tc.save({"conanfile.py": conanfile_bar})
+    tc.run("create .")
+    tc.save({"conanfile.py": conanfile_foo})
+    tc.run("create .")
+
+    create_layout = tc.created_layout()
+    cyclone_path = os.path.join(create_layout.metadata(), "sbom.cdx.json")
+    content = tc.load(cyclone_path)
+    content_json = json.loads(content)
+    if cyclone_version == 'cyclonedx_1_4':
+        assert content_json["metadata"]["component"]["author"]
+        assert content_json["metadata"]["component"]["type"] == 'application'
+        assert content_json["metadata"]["tools"][0]
+        assert content_json["components"][0]["author"]
+        assert content_json["components"][0]["type"] == 'application'
+    elif cyclone_version == 'cyclonedx_1_6':
+        assert not content_json["metadata"]["component"].get("author")
+        assert content_json["metadata"]["component"]["authors"]
+        assert content_json["metadata"]["component"]["type"] == 'application'
+        assert content_json["metadata"]["tools"]["components"][0]
+        assert not content_json["components"][0].get("author")
+        assert content_json["components"][0]["authors"]
+        assert content_json["components"][0]["type"] == 'application'
diff --git a/test/integration/command/info/info_test.py b/test/integration/command/info/info_test.py
index 59765ee41..9ef293701 100644
--- a/test/integration/command/info/info_test.py
+++ b/test/integration/command/info/info_test.py
@@ -357,6 +357,37 @@ class TestErrorsInGraph:
         assert "ERROR: Package 'dep/0.1' not resolved: dep/0.1: Cannot load" in c.out
         assert exit_code == ERROR_GENERAL
 
+    def test_missing_invalid(self):
+        c = TestClient()
+        dep_invalid = textwrap.dedent(
+            """
+           from conan import ConanFile
+           from conan.errors import ConanInvalidConfiguration
+           class Pkg(ConanFile):
+               name = "dep_invalid"
+               settings = "os", "build_type"
+               version = "0.1"
+               def validate(self):
+                   raise ConanInvalidConfiguration("Invalid package")
+        """
+        )
+
+        c.save({
+                "dep/conanfile.py": GenConanfile("dep").with_version("0.1"),
+                "dep_invalid/conanfile.py": dep_invalid,
+                "consumer/conanfile.py": GenConanfile("consumer")
+                                        .with_requires("dep/0.1")
+                                        .with_requires("dep_invalid/0.1"),
+            })
+        c.run("export dep")
+        c.run("export dep_invalid")
+        exit_code = c.run("graph info consumer")
+        assert "WARN: There are some error(s) in the graph:" in c.out
+        assert "- Missing packages: dep" in c.out
+        assert "- Invalid packages: dep_invalid" in c.out
+        assert exit_code == 0
+
+
 
 class TestInfoUpdate:
 
diff --git a/test/integration/command/info/test_info_build_order.py b/test/integration/command/info/test_info_build_order.py
index a9605cea5..4a39b80e6 100644
--- a/test/integration/command/info/test_info_build_order.py
+++ b/test/integration/command/info/test_info_build_order.py
@@ -804,9 +804,8 @@ def test_multi_configuration_profile_args():
 
 def test_build_order_space_in_options():
     tc = TestClient(light=True)
-    tc.save({"dep/conanfile.py": GenConanfile("dep", "1.0")
-                .with_option("flags", ["ANY", None])
-                .with_option("extras", ["ANY", None]),
+    tc.save({"dep/conanfile.py": GenConanfile("dep", "1.0").with_option("flags", ["ANY", None])
+                                                           .with_option("extras", ["ANY", None]),
              "conanfile.txt": textwrap.dedent("""
              [requires]
              dep/1.0
@@ -879,3 +878,25 @@ def test_build_order_build_context_compatible():
                                 "bar/1.0": ["5e4ffcc1ff33697a4ee96f66f0d2228ec458f25c", "Build"]})
         c.assert_listed_binary({"foo/1.0": ["4e2ae338231ae18d0d43b9e119404d2b2c416758", "Build"]},
                                build=True)
+
+
+def test_info_build_order_editable():
+    c = TestClient()
+    c.save({"dep/conanfile.py": GenConanfile("dep", "0.1"),
+            "pkg/conanfile.py": GenConanfile("pkg", "0.1").with_requires("dep/0.1"),
+            "consumer/conanfile.txt": "[requires]\npkg/0.1"})
+    c.run("editable add dep")
+    c.run("export pkg")
+    
+    c.run("graph build-order consumer --build=missing --build=editable -f=json --order-by=recipe")
+    bo_json = json.loads(c.stdout)
+    pkg = bo_json["order"][0][0]["packages"][0][0]
+    assert pkg["binary"] == "EditableBuild"
+    assert pkg["build_args"] == "--requires=dep/0.1 --build=dep/0.1"
+
+    c.run("graph build-order consumer --build=missing --build=editable -f=json "
+          "--order-by=configuration")
+    bo_json = json.loads(c.stdout)
+    pkg = bo_json["order"][0][0]
+    assert pkg["binary"] == "EditableBuild"
+    assert pkg["build_args"] == "--requires=dep/0.1 --build=dep/0.1"
diff --git a/test/integration/command/new_test.py b/test/integration/command/test_new.py
similarity index 100%
rename from test/integration/command/new_test.py
rename to test/integration/command/test_new.py
diff --git a/test/integration/command/test_profile.py b/test/integration/command/test_profile.py
index b71bbd6ac..a926b7629 100644
--- a/test/integration/command/test_profile.py
+++ b/test/integration/command/test_profile.py
@@ -2,7 +2,11 @@ import json
 import os
 import textwrap
 
+import pytest
+
+from conan.test.assets.genconanfile import GenConanfile
 from conan.test.utils.tools import TestClient
+from conans.util.files import save
 
 
 def test_profile_path():
@@ -151,3 +155,18 @@ def test_profile_show_json():
     # Check that tool_requires are properly serialized in json format
     # https://github.com/conan-io/conan/issues/15183
     assert profile["build"]["tool_requires"] == {'mytool/*': ["mytool/1.0"]}
+
+
+@pytest.mark.parametrize("new_global", [None, 0, 1, True, False, ""])
+def test_settings_serialize(new_global):
+    tc = TestClient(light=True)
+    settings_user = "new_global: [null, 0, 1, True, False, '']"
+    tc.save_home({"settings_user.yml": settings_user})
+    tc.save({"conanfile.py": GenConanfile("foo", "1.0").with_settings("new_global")})
+
+    s = f"-s new_global={new_global}" if new_global is not None else ""
+    tc.run(f"create . {s}")
+    if new_global is None:
+        assert "settings: new_global=" not in tc.out
+    else:
+        assert f"settings: new_global={new_global}" in tc.out
diff --git a/test/integration/conanfile/test_deprecated.py b/test/integration/conanfile/test_deprecated.py
index 128cc5362..d6a2b466c 100644
--- a/test/integration/conanfile/test_deprecated.py
+++ b/test/integration/conanfile/test_deprecated.py
@@ -1,5 +1,7 @@
 import textwrap
 
+import pytest
+
 from conan.test.utils.tools import TestClient, GenConanfile
 
 
@@ -63,3 +65,18 @@ class TestDeprecated:
 
         tc.run("graph info . --version=2.0")
         assert "deprecated: None" in tc.out
+
+class TestDeprecatedIncludesMessages:
+    @pytest.mark.parametrize("import_stm,way", [
+        ["from conans.model.package_ref import PkgReference", "PkgReference()"],
+        ["from conans.model.package_ref import PkgReference", "PkgReference.loads('foo/1.0:123')"],
+        ["from conans.model.recipe_ref import RecipeReference", "RecipeReference()"],
+        ["from conans.model.recipe_ref import RecipeReference", "RecipeReference.loads('foo/1.0@user/channel')"]
+    ])
+    def test_deprecated_package_ref_include(self, import_stm, way):
+        t = TestClient(light=True)
+        t.save({"conanfile.py": GenConanfile("pkg", "0.1")
+               .with_import(import_stm)
+                .with_package_id(f"ref = {way}")})
+        t.run("create conanfile.py")
+        assert f"Private '{import_stm}'" in t.out
diff --git a/test/integration/graph/test_subgraph_reports.py b/test/integration/graph/test_subgraph_reports.py
index 597fb6386..b01ecfe23 100644
--- a/test/integration/graph/test_subgraph_reports.py
+++ b/test/integration/graph/test_subgraph_reports.py
@@ -4,7 +4,7 @@ import textwrap
 
 from conan.test.assets.genconanfile import GenConanfile
 from conan.test.utils.tools import TestClient
-from conans.model.recipe_ref import RecipeReference
+from conan.api.model import RecipeReference
 from conans.util.files import load
 
 
diff --git a/test/integration/package_id/test_config_package_id.py b/test/integration/package_id/test_config_package_id.py
index e3a27714b..af3b7fbf7 100644
--- a/test/integration/package_id/test_config_package_id.py
+++ b/test/integration/package_id/test_config_package_id.py
@@ -20,6 +20,7 @@ def test_config_package_id(config_version, mode, result):
     save(c.paths.global_conf_path, f"core.package_id:config_mode={mode}")
     c.save({"conanfile.py": GenConanfile("pkg", "0.1")})
     c.run("create .")
+    assert f"config_version: {result}" in c.out
     c.run("list pkg/0.1:* --format=json")
     info = json.loads(c.stdout)
     rrev = info["Local Cache"]["pkg/0.1"]["revisions"]["485dad6cb11e2fa99d9afbe44a57a164"]
@@ -39,3 +40,16 @@ def test_error_config_package_id():
     c.run("create .", assert_error=True)
     assert "ERROR: core.package_id:config_mode defined, " \
            "but error while loading 'config_version.json'" in c.out
+
+@pytest.mark.parametrize("config_version, mode, result", [
+    ("myconfig/1.2.3#rev1:pid1#prev1", "minor_mode", "myconfig/1.2.Z"),
+    ("myconfig/1.2.3#rev1:pid1#prev1", "patch_mode", "myconfig/1.2.3"),
+])
+def test_config_package_id_clear(config_version, mode, result):
+    c = TestClient(light=True)
+    config_version = json.dumps({"config_version": [config_version]})
+    save(c.paths.config_version_path, config_version)
+    save(c.paths.global_conf_path, f"core.package_id:config_mode={mode}")
+    c.save({"conanfile.py": GenConanfile("pkg", "0.1").with_package_id("self.info.clear()")})
+    c.run("create .")
+    assert f"config_version: {result}" not in c.out
diff --git a/test/unittests/util/xz_test.py b/test/unittests/util/xz_test.py
index 707293410..f6fd540b2 100644
--- a/test/unittests/util/xz_test.py
+++ b/test/unittests/util/xz_test.py
@@ -20,3 +20,16 @@ def test_unzip_can_xz():
     content = load(os.path.join(dest_folder, "a_file.txt"))
     assert content == "my content!"
 
+
+def test_unzip_can_gz():
+    tmp_dir = temp_folder()
+    file_path = os.path.join(tmp_dir, "a_file.txt")
+    save(file_path, "my content!")
+    gz = os.path.join(tmp_dir, "sample.tar.gz")
+    with tarfile.open(gz, "w:gz") as tar:
+        tar.add(file_path, "a_file.txt")
+
+    dest_folder = temp_folder()
+    unzip(ConanFileMock(), gz, dest_folder)
+    content = load(os.path.join(dest_folder, "a_file.txt"))
+    assert content == "my content!"