Repository: conan-io/conan
Pull request #17296 (2024-11-08 15:50:41)

[cpp_info][BazelDeps] Improve auto deduce locations
Changelog: Feature: Improved auto deduce location function.
Changelog: Feature: BazelDeps using the new `deduce_location` mechanism to find the libraries.
Changelog: Bugfix: BazelDeps failed to find OpenSSL shared libraries.
Docs: omit
Close: https://github.com/conan-io/conan/issues/16990
Close: https://github.com/conan-io/conan/issues/16926


Patch:
diff --git a/conan/api/conan_api.py b/conan/api/conan_api.py
index cfa9740f9..0b280e5ed 100644
--- a/conan/api/conan_api.py
+++ b/conan/api/conan_api.py
@@ -5,6 +5,7 @@ from conan.api.subapi.cache import CacheAPI
 from conan.api.subapi.command import CommandAPI
 from conan.api.subapi.local import LocalAPI
 from conan.api.subapi.lockfile import LockfileAPI
+from conan.api.subapi.workspace import WorkspaceAPI
 from conan import conan_version
 from conan.api.subapi.config import ConfigAPI
 from conan.api.subapi.download import DownloadAPI
@@ -32,7 +33,8 @@ class ConanAPI:
             raise ConanException("Conan needs Python >= 3.6")
 
         init_colorama(sys.stderr)
-        self.cache_folder = cache_folder or get_conan_user_home()
+        self.workspace = WorkspaceAPI(self)
+        self.cache_folder = self.workspace.home_folder() or cache_folder or get_conan_user_home()
         self.home_folder = self.cache_folder  # Lets call it home, deprecate "cache"
 
         # Migration system
diff --git a/conan/api/subapi/config.py b/conan/api/subapi/config.py
index 57d0ec026..e1e5d9d3a 100644
--- a/conan/api/subapi/config.py
+++ b/conan/api/subapi/config.py
@@ -40,19 +40,18 @@ class ConfigAPI:
         configuration_install(app, path_or_url, verify_ssl, config_type=config_type, args=args,
                               source_folder=source_folder, target_folder=target_folder)
 
-    def install_pkg(self, ref, lockfile=None, force=False, remotes=None):
+    def install_pkg(self, ref, lockfile=None, force=False, remotes=None, profile=None):
         ConanOutput().warning("The 'conan config install-pkg' is experimental",
                               warn_tag="experimental")
         conan_api = self.conan_api
         remotes = conan_api.remotes.list() if remotes is None else remotes
-        # Ready to use profiles as inputs, but NOT using profiles yet, empty ones
-        profile_host = profile_build = conan_api.profiles.get_profile([])
+        profile_host = profile_build = profile or conan_api.profiles.get_profile([])
 
         app = ConanApp(self.conan_api)
 
         # Computation of a very simple graph that requires "ref"
         conanfile = app.loader.load_virtual(requires=[RecipeReference.loads(ref)])
-        consumer_definer(conanfile, profile_build, profile_host)
+        consumer_definer(conanfile, profile_host, profile_build)
         root_node = Node(ref=None, conanfile=conanfile, context=CONTEXT_HOST, recipe=RECIPE_VIRTUAL)
         root_node.is_conf = True
         update = ["*"]
diff --git a/conan/api/subapi/local.py b/conan/api/subapi/local.py
index 1b4c1962a..5cff434a6 100644
--- a/conan/api/subapi/local.py
+++ b/conan/api/subapi/local.py
@@ -17,6 +17,9 @@ class LocalAPI:
     def __init__(self, conan_api):
         self._conan_api = conan_api
         self.editable_packages = EditablePackages(conan_api.home_folder)
+        editables = conan_api.workspace.editables()
+        if editables:
+            self.editable_packages.edited_refs.update(editables)
 
     @staticmethod
     def get_conanfile_path(path, cwd, py):
@@ -55,17 +58,20 @@ class LocalAPI:
         target_path = self._conan_api.local.get_conanfile_path(path=path, cwd=cwd, py=True)
         output_folder = make_abs_path(output_folder) if output_folder else None
         # Check the conanfile is there, and name/version matches
-        self.editable_packages.add(ref, target_path, output_folder=output_folder)
+        editable_packages = EditablePackages(self._conan_api.home_folder)
+        editable_packages.add(ref, target_path, output_folder=output_folder)
         return ref
 
     def editable_remove(self, path=None, requires=None, cwd=None):
         if path:
             path = make_abs_path(path, cwd)
             path = os.path.join(path, "conanfile.py")
-        return self.editable_packages.remove(path, requires)
+        editable_packages = EditablePackages(self._conan_api.home_folder)
+        return editable_packages.remove(path, requires)
 
     def editable_list(self):
-        return self.editable_packages.edited_refs
+        editable_packages = EditablePackages(self._conan_api.home_folder)
+        return editable_packages.edited_refs
 
     def source(self, path, name=None, version=None, user=None, channel=None, remotes=None):
         """ calls the 'source()' method of the current (user folder) conanfile.py
@@ -116,4 +122,3 @@ class LocalAPI:
         conanfile = app.loader.load_named(conanfile_path, name=name, version=version, user=user,
                                           channel=channel, remotes=remotes, graph_lock=lockfile)
         return conanfile
-
diff --git a/conan/api/subapi/new.py b/conan/api/subapi/new.py
index b9732daea..8b3089a0c 100644
--- a/conan/api/subapi/new.py
+++ b/conan/api/subapi/new.py
@@ -93,9 +93,11 @@ class NewAPI:
     @staticmethod
     def render(template_files, definitions):
         result = {}
-        name = definitions.get("name", "Pkg")
+        name = definitions.get("name", "pkg")
         if isinstance(name, list):
             raise ConanException(f"name argument can't be multiple: {name}")
+        if name != name.lower():
+            raise ConanException(f"name argument must be lowercase: {name}")
         definitions["conan_version"] = __version__
 
         def ensure_list(key):
diff --git a/conan/api/subapi/workspace.py b/conan/api/subapi/workspace.py
new file mode 100644
index 000000000..3a5fe6c49
--- /dev/null
+++ b/conan/api/subapi/workspace.py
@@ -0,0 +1,82 @@
+import os
+import shutil
+
+from conan.cli import make_abs_path
+from conan.internal.conan_app import ConanApp
+from conan.internal.workspace import Workspace
+from conan.tools.scm import Git
+from conan.errors import ConanException
+from conans.client.graph.graph import RECIPE_EDITABLE
+from conans.client.source import retrieve_exports_sources
+from conans.model.recipe_ref import RecipeReference
+from conans.util.files import merge_directories
+
+
+class WorkspaceAPI:
+
+    def __init__(self, conan_api):
+        self._conan_api = conan_api
+        self._workspace = Workspace()
+
+    def home_folder(self):
+        return self._workspace.home_folder()
+
+    def folder(self):
+        return self._workspace.folder
+
+    def config_folder(self):
+        return self._workspace.config_folder()
+
+    def editables(self):
+        return self._workspace.editables()
+
+    def open(self, require, remotes, cwd=None):
+        app = ConanApp(self._conan_api)
+        ref = RecipeReference.loads(require)
+        recipe = app.proxy.get_recipe(ref, remotes, update=False, check_update=False)
+
+        layout, recipe_status, remote = recipe
+        if recipe_status == RECIPE_EDITABLE:
+            raise ConanException(f"Can't open a dependency that is already an editable: {ref}")
+        ref = layout.reference
+        conanfile_path = layout.conanfile()
+        conanfile, module = app.loader.load_basic_module(conanfile_path, remotes=remotes)
+
+        scm = conanfile.conan_data.get("scm") if conanfile.conan_data else None
+        dst_path = os.path.join(cwd or os.getcwd(), ref.name)
+        if scm is None:
+            conanfile.output.warning("conandata doesn't contain 'scm' information\n"
+                                     "doing a local copy!!!")
+            shutil.copytree(layout.export(), dst_path)
+            retrieve_exports_sources(app.remote_manager, layout, conanfile, ref, remotes)
+            export_sources = layout.export_sources()
+            if os.path.exists(export_sources):
+                conanfile.output.warning("There are export-sources, copying them, but the location"
+                                         " might be incorrect, use 'scm' approach")
+                merge_directories(export_sources, dst_path)
+        else:
+            git = Git(conanfile, folder=cwd)
+            git.clone(url=scm["url"], target=ref.name)
+            git.folder = ref.name  # change to the cloned folder
+            git.checkout(commit=scm["commit"])
+        return dst_path
+
+    def add(self, path, name=None, version=None, user=None, channel=None, cwd=None,
+            output_folder=None, remotes=None):
+        path = self._conan_api.local.get_conanfile_path(path, cwd, py=True)
+        app = ConanApp(self._conan_api)
+        conanfile = app.loader.load_named(path, name, version, user, channel, remotes=remotes)
+        if conanfile.name is None or conanfile.version is None:
+            raise ConanException("Editable package recipe should declare its name and version")
+        ref = RecipeReference(conanfile.name, conanfile.version, conanfile.user, conanfile.channel)
+        ref.validate_ref()
+        output_folder = make_abs_path(output_folder) if output_folder else None
+        # Check the conanfile is there, and name/version matches
+        self._workspace.add(ref, path, output_folder=output_folder)
+        return ref
+
+    def remove(self, path):
+        return self._workspace.remove(path)
+
+    def info(self):
+        return self._workspace.serialize()
diff --git a/conan/cli/commands/build.py b/conan/cli/commands/build.py
index aa62a740d..4720e6259 100644
--- a/conan/cli/commands/build.py
+++ b/conan/cli/commands/build.py
@@ -61,7 +61,6 @@ def build(conan_api, parser, *args):
     print_graph_packages(deps_graph)
 
     out = ConanOutput()
-    out.title("Installing packages")
     conan_api.install.install_binaries(deps_graph=deps_graph, remotes=remotes)
 
     out.title("Finalizing install (deploy, generators)")
diff --git a/conan/cli/commands/config.py b/conan/cli/commands/config.py
index 51b18fbb9..9aeaa2e5f 100644
--- a/conan/cli/commands/config.py
+++ b/conan/cli/commands/config.py
@@ -73,14 +73,23 @@ def config_install_pkg(conan_api, parser, subparser, *args):
     subparser.add_argument("--url", action=OnceArgument,
                            help="(Experimental) Provide Conan repository URL "
                                 "(for first install without remotes)")
+    subparser.add_argument("-pr", "--profile", help="Profile to install config")
+    subparser.add_argument("-s", "--settings", action="append", help="Settings to install config")
+    subparser.add_argument("-o", "--options", action="append", help="Options to install config")
     args = parser.parse_args(*args)
 
     lockfile = conan_api.lockfile.get_lockfile(lockfile=args.lockfile,
                                                partial=args.lockfile_partial)
 
+    try:
+        default_profile = args.profile or conan_api.profiles.get_default_build()
+    except ConanException:  # it can fail if the default profile doesn't exist yet
+        default_profile = None
+    profiles = [default_profile] if default_profile else []
+    profile = conan_api.profiles.get_profile(profiles, args.settings, args.options)
     remotes = [Remote("_tmp_conan_config", url=args.url)] if args.url else None
     config_pref = conan_api.config.install_pkg(args.item, lockfile=lockfile, force=args.force,
-                                               remotes=remotes)
+                                               remotes=remotes, profile=profile)
     lockfile = conan_api.lockfile.add_lockfile(lockfile, config_requires=[config_pref.ref])
     conan_api.lockfile.save_lockfile(lockfile, args.lockfile_out)
 
diff --git a/conan/cli/commands/list.py b/conan/cli/commands/list.py
index 41c383692..da032afc9 100644
--- a/conan/cli/commands/list.py
+++ b/conan/cli/commands/list.py
@@ -233,7 +233,7 @@ def list(conan_api: ConanAPI, parser, *args):
     args = parser.parse_args(*args)
 
     if args.pattern is None and args.graph is None:
-        raise ConanException("Missing pattern or graph json file")
+        args.pattern = "*"  # All packages
     if args.graph:  # a few arguments are not compatible with this
         if args.pattern:
             raise ConanException("Cannot define both the pattern and the graph json file")
diff --git a/conan/cli/commands/new.py b/conan/cli/commands/new.py
index 18b3b0c96..ecc961589 100644
--- a/conan/cli/commands/new.py
+++ b/conan/cli/commands/new.py
@@ -24,6 +24,8 @@ 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("-o", "--output", help="Output folder for the generated files",
+                        default=os.getcwd())
 
     args = parser.parse_args(*args)
     # Manually parsing the remainder
@@ -59,10 +61,10 @@ def new(conan_api, parser, *args):
 
     # Saving the resulting files
     output = ConanOutput()
-    cwd = os.getcwd()
+    output_folder = args.output
     # Making sure they don't overwrite existing files
     for f, v in sorted(template_files.items()):
-        path = os.path.join(cwd, f)
+        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)
@@ -70,7 +72,7 @@ def new(conan_api, parser, *args):
 
     # copy non-templates
     for f, v in sorted(non_template_files.items()):
-        path = os.path.join(cwd, f)
+        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)
diff --git a/conan/cli/commands/workspace.py b/conan/cli/commands/workspace.py
new file mode 100644
index 000000000..bbf19b607
--- /dev/null
+++ b/conan/cli/commands/workspace.py
@@ -0,0 +1,108 @@
+import json
+import os
+
+from conan.api.conan_api import ConanAPI
+from conan.api.output import ConanOutput, cli_out_write
+from conan.cli import make_abs_path
+from conan.cli.args import add_reference_args
+from conan.cli.command import conan_command, conan_subcommand
+from conan.cli.commands.list import print_serial
+from conan.errors import ConanException
+
+
+@conan_subcommand(formatters={"text": cli_out_write})
+def workspace_root(conan_api: ConanAPI, parser, subparser, *args):
+    """
+    Return the folder containing the conanws.py/conanws.yml workspace file
+    """
+    ws = conan_api.workspace
+    if not ws.folder():
+        raise ConanException("No workspace defined, conanws.py file not found")
+    return ws.folder()
+
+
+@conan_subcommand()
+def workspace_open(conan_api: ConanAPI, parser, subparser, *args):
+    """
+    Open specific references
+    """
+    subparser.add_argument("reference",
+                           help="Open this package source repository")
+    group = subparser.add_mutually_exclusive_group()
+    group.add_argument("-r", "--remote", action="append", default=None,
+                       help='Look in the specified remote or remotes server')
+    group.add_argument("-nr", "--no-remote", action="store_true",
+                       help='Do not use remote, resolve exclusively in the cache')
+    args = parser.parse_args(*args)
+    remotes = conan_api.remotes.list(args.remote) if not args.no_remote else []
+    cwd = os.getcwd()
+    conan_api.workspace.open(args.reference, remotes=remotes, cwd=cwd)
+
+
+@conan_subcommand()
+def workspace_add(conan_api: ConanAPI, parser, subparser, *args):
+    """
+    Add packages to current workspace
+    """
+    subparser.add_argument('path',  nargs="?",
+                           help='Path to the package folder in the user workspace')
+    add_reference_args(subparser)
+    subparser.add_argument("--ref", nargs="?",
+                           help="Open and add this reference")
+    subparser.add_argument("-of", "--output-folder",
+                           help='The root output folder for generated and build files')
+    group = subparser.add_mutually_exclusive_group()
+    group.add_argument("-r", "--remote", action="append", default=None,
+                       help='Look in the specified remote or remotes server')
+    group.add_argument("-nr", "--no-remote", action="store_true",
+                       help='Do not use remote, resolve exclusively in the cache')
+    args = parser.parse_args(*args)
+    if args.path and args.ref:
+        raise ConanException("Do not use both 'path' and '--ref' argument")
+    remotes = conan_api.remotes.list(args.remote) if not args.no_remote else []
+    cwd = os.getcwd()
+    path = args.path
+    if args.ref:
+        # TODO: Use path here to open in this path
+        path = conan_api.workspace.open(args.ref, remotes, cwd=cwd)
+    ref = conan_api.workspace.add(path,
+                                  args.name, args.version, args.user, args.channel,
+                                  cwd, args.output_folder, remotes=remotes)
+    ConanOutput().success("Reference '{}' added to workspace".format(ref))
+
+
+@conan_subcommand()
+def workspace_remove(conan_api: ConanAPI, parser, subparser, *args):
+    """
+    Remove packages to current workspace
+    """
+    subparser.add_argument('path', help='Path to the package folder in the user workspace')
+    args = parser.parse_args(*args)
+    removed = conan_api.workspace.remove(make_abs_path(args.path))
+    ConanOutput().info(f"Removed from workspace: {removed}")
+
+
+def print_json(data):
+    results = data["info"]
+    myjson = json.dumps(results, indent=4)
+    cli_out_write(myjson)
+
+
+def _print_workspace_info(data):
+    print_serial(data["info"])
+
+
+@conan_subcommand(formatters={"text": _print_workspace_info, "json": print_json})
+def workspace_info(conan_api: ConanAPI, parser, subparser, *args):
+    """
+    Display info for current workspace
+    """
+    parser.parse_args(*args)
+    return {"info": conan_api.workspace.info()}
+
+
+@conan_command(group="Consumer")
+def workspace(conan_api, parser, *args):
+    """
+    Manage Conan workspaces (group of packages in editable mode)
+    """
diff --git a/conan/cli/formatters/graph/info_graph_html.py b/conan/cli/formatters/graph/info_graph_html.py
index 67db93e2f..60a31c28f 100644
--- a/conan/cli/formatters/graph/info_graph_html.py
+++ b/conan/cli/formatters/graph/info_graph_html.py
@@ -24,7 +24,7 @@ graph_info_html = r"""
         </style>
 
         <div style="display: grid; grid-template-columns: 75% 25%; grid-template-rows: 30px auto; height: 100vh;">
-            <div id="mylegend" style="background-color: lightgrey; grid-column-end: span 2;"></div>
+            <div id="mylegend" style="background-color: lightgrey; grid-column-end: span 2;height: 100%"></div>
             <div id="mynetwork"></div>
             <div style="background-color: lightgrey;min-height:100%;height:0;overflow-y: auto;">
                 <div>
diff --git a/conan/cli/printers/graph.py b/conan/cli/printers/graph.py
index ddbd95326..931848037 100644
--- a/conan/cli/printers/graph.py
+++ b/conan/cli/printers/graph.py
@@ -91,6 +91,7 @@ def print_graph_basic(graph):
                     output.info(f"        {src_ref}->{option}={conflict_value}", Color.BRIGHT_CYAN)
         output.info("    It is recommended to define options values in profiles, not in recipes",
                     Color.BRIGHT_CYAN)
+        output.warning("There are options conflicts in the dependency graph", warn_tag="risk")
 
 
 def print_graph_packages(graph):
diff --git a/conan/internal/api/new/bazel_7_lib.py b/conan/internal/api/new/bazel_7_lib.py
index fdc51f6d6..2cc08df3c 100644
--- a/conan/internal/api/new/bazel_7_lib.py
+++ b/conan/internal/api/new/bazel_7_lib.py
@@ -49,7 +49,10 @@ class {{package_name}}Recipe(ConanFile):
         copy(self, "*.so", build, dest_lib, keep_path=False)
         copy(self, "*.dll", build, dest_bin, keep_path=False)
         copy(self, "*.dylib", build, dest_lib, keep_path=False)
-        copy(self, "*.a", build, dest_lib, keep_path=False)
+        if self.settings.os == "Linux" and self.options.get_safe("fPIC"):
+            copy(self, "*.pic.a", build, dest_lib, keep_path=False)
+        else:
+            copy(self, "*.a", build, dest_lib, keep_path=False)
         copy(self, "*.lib", build, dest_lib, keep_path=False)
         copy(self, "{{name}}.h", os.path.join(self.source_folder, "main"),
              os.path.join(self.package_folder, "include"), keep_path=False)
diff --git a/conan/internal/api/new/bazel_lib.py b/conan/internal/api/new/bazel_lib.py
index d888f7f7d..d4b375bb7 100644
--- a/conan/internal/api/new/bazel_lib.py
+++ b/conan/internal/api/new/bazel_lib.py
@@ -53,7 +53,10 @@ class {{package_name}}Recipe(ConanFile):
         copy(self, "*.so", build, dest_lib, keep_path=False)
         copy(self, "*.dll", build, dest_bin, keep_path=False)
         copy(self, "*.dylib", build, dest_lib, keep_path=False)
-        copy(self, "*.a", build, dest_lib, keep_path=False)
+        if self.settings.os == "Linux" and self.options.get_safe("fPIC"):
+            copy(self, "*.pic.a", build, dest_lib, keep_path=False)
+        else:
+            copy(self, "*.a", build, dest_lib, keep_path=False)
         copy(self, "*.lib", build, dest_lib, keep_path=False)
         copy(self, "{{name}}.h", os.path.join(self.source_folder, "main"),
              os.path.join(self.package_folder, "include"), keep_path=False)
diff --git a/conan/internal/cache/db/cache_database.py b/conan/internal/cache/db/cache_database.py
index 56b26688d..6cd38b7c7 100644
--- a/conan/internal/cache/db/cache_database.py
+++ b/conan/internal/cache/db/cache_database.py
@@ -22,9 +22,7 @@ class CacheDatabase:
             self._packages.create_table()
 
     def exists_prev(self, ref):
-        # TODO: This logic could be done directly against DB
-        matching_prevs = self.get_package_revisions_references(ref)
-        return len(matching_prevs) > 0
+        return self._packages.get_package_revisions_reference_exists(ref)
 
     def get_latest_package_reference(self, ref):
         prevs = self.get_package_revisions_references(ref, True)
@@ -61,11 +59,9 @@ class CacheDatabase:
         self._packages.remove_build_id(pref)
 
     def get_matching_build_id(self, ref, build_id):
-        # TODO: This can also be done in a single query in DB
-        for d in self._packages.get_package_references(ref):
-            existing_build_id = d["build_id"]
-            if existing_build_id == build_id:
-                return d["pref"]
+        result = self._packages.get_package_references_with_build_id_match(ref, build_id)
+        if result:
+            return result["pref"]
         return None
 
     def get_recipe(self, ref: RecipeReference):
diff --git a/conan/internal/cache/db/packages_table.py b/conan/internal/cache/db/packages_table.py
index 9f066d9b7..9dceb3fa1 100644
--- a/conan/internal/cache/db/packages_table.py
+++ b/conan/internal/cache/db/packages_table.py
@@ -171,6 +171,22 @@ class PackagesDBTable(BaseDbTable):
             for row in r.fetchall():
                 yield self._as_dict(self.row_type(*row))
 
+    def get_package_revisions_reference_exists(self, pref: PkgReference):
+        assert pref.ref.revision, "To check package revision existence you must provide a recipe revision."
+        assert pref.package_id, "To check package revisions existence you must provide a package id."
+        check_prev = f"AND {self.columns.prev} = '{pref.revision}' " if pref.revision else ""
+        query = f'SELECT 1 FROM {self.table_name} ' \
+                f"WHERE {self.columns.rrev} = '{pref.ref.revision}' " \
+                f"AND {self.columns.reference} = '{str(pref.ref)}' " \
+                f"AND {self.columns.pkgid} = '{pref.package_id}' " \
+                f'{check_prev} ' \
+                f'AND {self.columns.prev} IS NOT NULL ' \
+                'LIMIT 1 '
+        with self.db_connection() as conn:
+            r = conn.execute(query)
+            row = r.fetchone()
+            return bool(row)
+
     def get_package_references(self, ref: RecipeReference, only_latest_prev=True):
         # Return the latest revisions
         assert ref.revision, "To search for package id's you must provide a recipe revision."
@@ -198,3 +214,29 @@ class PackagesDBTable(BaseDbTable):
             r = conn.execute(query)
             for row in r.fetchall():
                 yield self._as_dict(self.row_type(*row))
+
+    def get_package_references_with_build_id_match(self, ref: RecipeReference, build_id):
+        # Return the latest revisions
+        assert ref.revision, "To search for package id's by build_id you must provide a recipe revision."
+        # we select the latest prev for each package_id
+        # This is the same query as get_package_references, but with an additional filter
+        query = f'SELECT {self.columns.reference}, ' \
+                f'{self.columns.rrev}, ' \
+                f'{self.columns.pkgid}, ' \
+                f'{self.columns.prev}, ' \
+                f'{self.columns.path}, ' \
+                f'MAX({self.columns.timestamp}), ' \
+                f'{self.columns.build_id}, ' \
+                f'{self.columns.lru} ' \
+                f'FROM {self.table_name} ' \
+                f"WHERE {self.columns.rrev} = '{ref.revision}' " \
+                f"AND {self.columns.reference} = '{str(ref)}' " \
+                f"AND {self.columns.build_id} = '{build_id}' " \
+                f'GROUP BY {self.columns.pkgid} '
+
+        with self.db_connection() as conn:
+            r = conn.execute(query)
+            row = r.fetchone()
+            if row:
+                return self._as_dict(self.row_type(*row))
+            return None
diff --git a/conan/internal/deploy.py b/conan/internal/deploy.py
index 977bb0744..c2ff04982 100644
--- a/conan/internal/deploy.py
+++ b/conan/internal/deploy.py
@@ -57,7 +57,7 @@ def do_deploys(conan_api, graph, deploy, deploy_package, deploy_folder):
             conanfile = node.conanfile
             if not conanfile.ref:  # virtual or conanfile.txt, can't have deployer
                 continue
-            consumer = conanfile._conan_is_consumer
+            consumer = conanfile._conan_is_consumer  # noqa
             if any(conanfile.ref.matches(p, consumer) for p in excluded):
                 continue
             if not any(conanfile.ref.matches(p, consumer) for p in included):
diff --git a/conan/internal/methods.py b/conan/internal/methods.py
index c8405bf55..17050dbef 100644
--- a/conan/internal/methods.py
+++ b/conan/internal/methods.py
@@ -93,7 +93,7 @@ def run_configure_method(conanfile, down_options, profile_options, ref):
     auto_language(conanfile)  # default implementation removes `compiler.cstd`
 
     # Assign only the current package options values, but none of the dependencies
-    is_consumer = conanfile._conan_is_consumer
+    is_consumer = conanfile._conan_is_consumer  # noqa
     conanfile.options.apply_downstream(down_options, profile_options, ref, is_consumer)
 
     if hasattr(conanfile, "configure"):
diff --git a/conan/internal/workspace.py b/conan/internal/workspace.py
new file mode 100644
index 000000000..74cc1ffbb
--- /dev/null
+++ b/conan/internal/workspace.py
@@ -0,0 +1,154 @@
+import os
+from pathlib import Path
+
+import yaml
+
+from conan.api.output import ConanOutput
+from conans.client.loader import load_python_file
+from conan.errors import ConanException
+from conans.model.recipe_ref import RecipeReference
+from conans.util.files import load, save
+
+
+def _find_ws_folder():
+    path = Path(os.getcwd())
+    while path.is_dir() and len(path.parts) > 1:  # finish at '/'
+        if (path / "conanws.yml").is_file() or (path / "conanws.py").is_file():
+            return str(path)
+        else:
+            path = path.parent
+
+
+class _UserWorkspaceAPI:
+    def __init__(self, folder):
+        self.folder = folder
+
+    def load(self, conanfile_path):
+        conanfile_path = os.path.join(self.folder, conanfile_path)
+        from conans.client.loader import ConanFileLoader
+        loader = ConanFileLoader(pyreq_loader=None, conanfile_helpers=None)
+        conanfile = loader.load_named(conanfile_path, name=None, version=None, user=None,
+                                      channel=None, remotes=None, graph_lock=None)
+        return conanfile
+
+
+class Workspace:
+    TEST_ENABLED = False
+
+    def __init__(self):
+        self._folder = _find_ws_folder()
+        if self._folder:
+            ConanOutput().warning(f"Workspace found: {self._folder}")
+            if (Workspace.TEST_ENABLED or os.getenv("CONAN_WORKSPACE_ENABLE")) != "will_break_next":
+                ConanOutput().warning("Workspace ignored as CONAN_WORKSPACE_ENABLE is not set")
+                self._folder = None
+            else:
+                ConanOutput().warning(f"Workspace is a dev-only feature, exclusively for testing")
+
+        self._yml = None
+        self._py = None
+        if self._folder is not None:
+            self._yml_file = os.path.join(self._folder, "conanws.yml")
+            if os.path.exists(self._yml_file):
+                try:
+                    self._yml = yaml.safe_load(load(self._yml_file))
+                except Exception as e:
+                    raise ConanException(f"Invalid workspace yml format at {self._folder}: {e}")
+
+            py_file = os.path.join(self._folder, "conanws.py")
+            if os.path.exists(py_file):
+                self._py, _ = load_python_file(py_file)
+                setattr(self._py, "workspace_api", _UserWorkspaceAPI(self._folder))
+                setattr(self._py, "conanws_data", self._yml)
+
+    @property
+    def name(self):
+        return self._attr("name") or os.path.basename(self._folder)
+
+    @property
+    def folder(self):
+        return self._folder
+
+    def _attr(self, value):
+        if self._py and getattr(self._py, value, None):
+            attr = getattr(self._py, value)
+            return attr() if callable(attr) else attr
+        if self._yml:
+            return self._yml.get(value)
+
+    def home_folder(self):
+        if not self._folder:
+            return
+        home = self._attr("home_folder")
+        if home is None or os.path.isabs(home):
+            return home
+        return os.path.normpath(os.path.join(self._folder, home))
+
+    def config_folder(self):
+        folder = self._attr("config_folder")
+        if folder is None or os.path.isabs(folder):
+            return folder
+        return os.path.normpath(os.path.join(self._folder, folder))
+
+    def _check_ws(self):
+        if not self._folder:
+            raise ConanException("Workspace not defined, please create a "
+                                 "'conanws.py' or 'conanws.yml' file")
+
+    def add(self, ref, path, output_folder):
+        """
+        Add a new editable to the current workspace 'conanws.yml' file.
+        If existing, the 'conanws.py' must use this via 'conanws_data' attribute
+        """
+        self._check_ws()
+        self._yml = self._yml or {}
+        editable = {"path": self._rel_path(path)}
+        if output_folder:
+            editable["output_folder"] = self._rel_path(output_folder)
+        self._yml.setdefault("editables", {})[str(ref)] = editable
+        save(self._yml_file, yaml.dump(self._yml))
+
+    def _rel_path(self, path):
+        if path is None:
+            return None
+        if not os.path.isabs(path):
+            raise ConanException(f"Editable path must be absolute: {path}")
+        path = os.path.relpath(path, self._folder)
+        if path.startswith(".."):
+            raise ConanException(f"Editable path must be inside the workspace folder: "
+                                 f"{self._folder}")
+        return path.replace("\\", "/")  # Normalize to unix path
+
+    def remove(self, path):
+        self._check_ws()
+        self._yml = self._yml or {}
+        found_ref = None
+        path = self._rel_path(path)
+        for ref, info in self._yml.get("editables", {}).items():
+            if os.path.dirname(info["path"]).replace("\\", "/") == path:
+                found_ref = ref
+                break
+        if not found_ref:
+            raise ConanException(f"No editable package to remove from this path: {path}")
+        self._yml["editables"].pop(found_ref)
+        save(self._yml_file, yaml.dump(self._yml))
+        return found_ref
+
+    def editables(self):
+        if not self._folder:
+            return
+        editables = self._attr("editables")
+        if editables:
+            editables = {RecipeReference.loads(r): v.copy() for r, v in editables.items()}
+            for v in editables.values():
+                v["path"] = os.path.normpath(os.path.join(self._folder, v["path"]))
+                if v.get("output_folder"):
+                    v["output_folder"] = os.path.normpath(os.path.join(self._folder,
+                                                                       v["output_folder"]))
+        return editables
+
+    def serialize(self):
+        self._check_ws()
+        return {"name": self.name,
+                "folder": self._folder,
+                "editables": self._attr("editables")}
diff --git a/conan/tools/cmake/cmake.py b/conan/tools/cmake/cmake.py
index a94bae711..ae3acd5ca 100644
--- a/conan/tools/cmake/cmake.py
+++ b/conan/tools/cmake/cmake.py
@@ -19,8 +19,8 @@ def _cmake_cmd_line_args(conanfile, generator):
         args.append("-j{}".format(njobs))
 
     maxcpucount = conanfile.conf.get("tools.microsoft.msbuild:max_cpu_count", check_type=int)
-    if maxcpucount and "Visual Studio" in generator:
-        args.append("/m:{}".format(maxcpucount))
+    if maxcpucount is not None and "Visual Studio" in generator:
+        args.append(f"/m:{maxcpucount}" if maxcpucount > 0 else "/m")
 
     # Arguments for verbosity
     if "Visual Studio" in generator:
diff --git a/conan/tools/cmake/cmakedeps/cmakedeps.py b/conan/tools/cmake/cmakedeps/cmakedeps.py
index d93659c91..2439a9467 100644
--- a/conan/tools/cmake/cmakedeps/cmakedeps.py
+++ b/conan/tools/cmake/cmakedeps/cmakedeps.py
@@ -105,7 +105,7 @@ class CMakeDeps(object):
                 msg.append(f"    find_package({config.file_name})")
             targets = ' '.join(c.root_target_name for c in direct_configs)
             msg.append(f"    target_link_libraries(... {targets})")
-            if self._conanfile._conan_is_consumer:
+            if self._conanfile._conan_is_consumer:  # noqa
                 self._conanfile.output.info("\n".join(msg), fg=Color.CYAN)
             else:
                 self._conanfile.output.verbose("\n".join(msg))
diff --git a/conan/tools/cmake/cmakedeps2/cmakedeps.py b/conan/tools/cmake/cmakedeps2/cmakedeps.py
index bdc6bba6f..0847d1848 100644
--- a/conan/tools/cmake/cmakedeps2/cmakedeps.py
+++ b/conan/tools/cmake/cmakedeps2/cmakedeps.py
@@ -6,6 +6,7 @@ from jinja2 import Template
 
 from conan.api.output import Color
 from conan.internal import check_duplicated_generator
+from conan.internal.api.install.generators import relativize_path
 from conan.tools.cmake.cmakedeps2.config import ConfigTemplate2
 from conan.tools.cmake.cmakedeps2.config_version import ConfigVersionTemplate2
 from conan.tools.cmake.cmakedeps2.target_configuration import TargetConfigurationTemplate2
@@ -85,10 +86,7 @@ class CMakeDeps2:
                     targets.append(target_name or f"{dep.ref.name}::{dep.ref.name}")
             if targets:
                 msg.append(f"    target_link_libraries(... {' '.join(targets)})")
-            if self._conanfile._conan_is_consumer:
-                self._conanfile.output.info("\n".join(msg), fg=Color.CYAN)
-            else:
-                self._conanfile.output.verbose("\n".join(msg))
+            self._conanfile.output.info("\n".join(msg), fg=Color.CYAN)
 
     def set_property(self, dep, prop, value, build_context=False):
         """
@@ -119,8 +117,11 @@ class CMakeDeps2:
         except KeyError:
             # Here we are not using the cpp_info = deduce_cpp_info(dep) because it is not
             # necessary for the properties
-            return dep.cpp_info.get_property(prop, check_type=check_type) if not comp_name \
-                else dep.cpp_info.components[comp_name].get_property(prop, check_type=check_type)
+            if not comp_name:
+                return dep.cpp_info.get_property(prop, check_type=check_type)
+            comp = dep.cpp_info.components.get(comp_name)  # it is a default dict
+            if comp is not None:
+                return comp.get_property(prop, check_type=check_type)
 
     def get_cmake_filename(self, dep, module_mode=None):
         """Get the name of the file for the find_package(XXX)"""
@@ -150,6 +151,13 @@ class CMakeDeps2:
         return get_transitive_requires(self._conanfile, conanfile)
 
 
+# TODO: Repeated from CMakeToolchain blocks
+def _join_paths(conanfile, paths):
+    paths = [p.replace('\\', '/').replace('$', '\\$').replace('"', '\\"') for p in paths]
+    paths = [relativize_path(p, conanfile, "${CMAKE_CURRENT_LIST_DIR}") for p in paths]
+    return " ".join([f'"{p}"' for p in paths])
+
+
 class _PathGenerator:
     _conan_cmakedeps_paths = "conan_cmakedeps_paths.cmake"
 
@@ -165,6 +173,9 @@ class _PathGenerator:
             {% if host_runtime_dirs %}
             set(CONAN_RUNTIME_LIB_DIRS {{ host_runtime_dirs }} )
             {% endif %}
+            {% if cmake_program_path %}
+            list(PREPEND CMAKE_PROGRAM_PATH {{ cmake_program_path }})
+            {% endif %}
             """)
 
         host_req = self._conanfile.dependencies.host
@@ -191,17 +202,36 @@ class _PathGenerator:
                     build_dir = dep.package_folder
                 pkg_folder = build_dir.replace("\\", "/") if build_dir else None
                 if pkg_folder:
-                    config_file = ConfigTemplate2(self._cmakedeps, dep).filename
-                    if os.path.isfile(os.path.join(pkg_folder, config_file)):
-                        pkg_paths[pkg_name] = pkg_folder
+                    f = self._cmakedeps.get_cmake_filename(dep)
+                    for filename in (f"{f}-config.cmake", f"{f}Config.cmake"):
+                        if os.path.isfile(os.path.join(pkg_folder, filename)):
+                            pkg_paths[pkg_name] = pkg_folder
                 continue
 
             # If CMakeDeps generated, the folder is this one
             # content.append(f'set({pkg_name}_ROOT "{gen_folder}")')
             pkg_paths[pkg_name] = "${CMAKE_CURRENT_LIST_DIR}"
 
+        # CMAKE_PROGRAM_PATH
+        cmake_program_path = {}
+        for req, dep in list(host_req.items()) + list(test_req.items()) + list(build_req.items()):
+            if not req.direct:
+                continue
+            cppinfo = dep.cpp_info.aggregated_components()
+            if not cppinfo.bindirs:
+                continue
+            previous = cmake_program_path.get(req.ref.name)
+            if previous:
+                self._conanfile.output.info(f"There is already a '{req.ref}' package "
+                                            f"contributing to CMAKE_PROGRAM_PATH. The one with "
+                                            f"build={req.build} test={req.test} will be used")
+
+            cmake_program_path[req.ref.name] = cppinfo.bindirs
+        cmake_program_path = [d for dirs in cmake_program_path.values() for d in dirs]
+
         context = {"host_runtime_dirs": self._get_host_runtime_dirs(),
-                   "pkg_paths": pkg_paths}
+                   "pkg_paths": pkg_paths,
+                   "cmake_program_path": _join_paths(self._conanfile, cmake_program_path)}
         content = Template(template, trim_blocks=True, lstrip_blocks=True).render(context)
         save(self._conanfile, self._conan_cmakedeps_paths, content)
 
diff --git a/conan/tools/cmake/cmakedeps2/config.py b/conan/tools/cmake/cmakedeps2/config.py
index 459d3b421..f31f3e67d 100644
--- a/conan/tools/cmake/cmakedeps2/config.py
+++ b/conan/tools/cmake/cmakedeps2/config.py
@@ -33,7 +33,28 @@ class ConfigTemplate2:
         # FIXME: Proper escaping of paths for CMake and relativization
         # FIXME: build_module_paths coming from last config only
         build_modules_paths = [f.replace("\\", "/") for f in build_modules_paths]
-        return {"pkg_name": pkg_name,
+        components = self._cmakedeps.get_property("cmake_components", self._conanfile,
+                                                  check_type=list)
+        if components is None:  # Lets compute the default components names
+            components = []
+            # This assumes that cmake_components is only defined with not multi .libs=[lib1, lib2]
+            for name in self._conanfile.cpp_info.components:
+                if name.startswith("_"):  # Skip private components
+                    continue
+                comp_components = self._cmakedeps.get_property("cmake_components", self._conanfile,
+                                                               name, check_type=list)
+                if comp_components:
+                    components.extend(comp_components)
+                else:
+                    cmakename = self._cmakedeps.get_property("cmake_target_name", self._conanfile,
+                                                             name)
+                    if cmakename and "::" in cmakename:  # Remove package namespace
+                        cmakename = cmakename.split("::", 1)[1]
+                    components.append(cmakename or name)
+        components = " ".join(components) if components else ""
+        return {"filename": f,
+                "components": components,
+                "pkg_name": pkg_name,
                 "targets_include_file": targets_include,
                 "build_modules_paths": build_modules_paths}
 
@@ -58,4 +79,16 @@ class ConfigTemplate2:
         message(STATUS "Conan: Including build module from '{{build_module}}'")
         include("{{ build_module }}")
         {% endfor %}
+
+        {% if components %}
+        set({{filename}}_PACKAGE_PROVIDED_COMPONENTS {{components}})
+        foreach(comp {%raw%}${{%endraw%}{{filename}}_FIND_COMPONENTS})
+          if(NOT ${comp} IN_LIST {{filename}}_PACKAGE_PROVIDED_COMPONENTS)
+            if({%raw%}${{%endraw%}{{filename}}_FIND_REQUIRED_${comp}})
+              message(STATUS "Conan: Error: '{{pkg_name}}' required COMPONENT '${comp}' not found")
+              set({{filename}}_FOUND FALSE)
+            endif()
+          endif()
+        endforeach()
+        {% endif %}
         """)
diff --git a/conan/tools/cmake/cmakedeps2/targets.py b/conan/tools/cmake/cmakedeps2/targets.py
index 2b934f825..e02d2926d 100644
--- a/conan/tools/cmake/cmakedeps2/targets.py
+++ b/conan/tools/cmake/cmakedeps2/targets.py
@@ -32,7 +32,7 @@ class TargetsTemplate2:
     @property
     def _template(self):
         return textwrap.dedent("""\
-            message(STATUS "Configuring Targets for {{ ref }}")
+            message(STATUS "Conan: Configuring Targets for {{ ref }}")
 
             # Load information for each installed configuration.
             file(GLOB _target_files "${CMAKE_CURRENT_LIST_DIR}/{{filename}}-Targets-*.cmake")
diff --git a/conan/tools/google/bazeldeps.py b/conan/tools/google/bazeldeps.py
index 3aac16e31..c9d6363ff 100644
--- a/conan/tools/google/bazeldeps.py
+++ b/conan/tools/google/bazeldeps.py
@@ -1,17 +1,17 @@
 import os
 import re
 import textwrap
-from collections import namedtuple
+from collections import namedtuple, defaultdict
 
 from jinja2 import Template, StrictUndefined
 
 from conan.errors import ConanException
 from conan.internal import check_duplicated_generator
 from conans.model.dependencies import get_transitive_requires
+from conans.model.pkg_type import PackageType
 from conans.util.files import save
 
 _BazelTargetInfo = namedtuple("DepInfo", ['repository_name', 'name', 'ref_name', 'requires', 'cpp_info'])
-_LibInfo = namedtuple("LibInfo", ['name', 'is_shared', 'lib_path', 'import_lib_path'])
 
 
 def _get_name_with_namespace(namespace, name):
@@ -72,100 +72,6 @@ def _get_requirements(conanfile, build_context_activated):
         yield require, dep
 
 
-def _get_libs(dep, cpp_info=None) -> list:
-    """
-    Get the static/shared library paths
-
-    :param dep: normally a <ConanFileInterface obj>
-    :param cpp_info: <CppInfo obj> of the component.
-    :return: list of tuples per static/shared library ->
-             [(name, is_shared, lib_path, import_lib_path)]
-             Note: ``library_path`` could be both static and shared ones in case of UNIX systems.
-                    Windows would have:
-                        * shared: library_path as DLL, and import_library_path as LIB
-                        * static: library_path as LIB, and import_library_path as None
-    """
-    def _is_shared():
-        """
-        Checking traits and shared option
-        """
-        default_value = dep.options.get_safe("shared") if dep.options else False
-        return {"shared-library": True,
-                "static-library": False}.get(str(dep.package_type), default_value)
-
-    def _save_lib_path(file_name, file_path):
-        """Add each lib with its full library path"""
-        name, ext = file_name.split('.', maxsplit=1)  # ext could be .if.lib
-        formatted_path = file_path.replace("\\", "/")
-        lib_name = None
-        # Users may not name their libraries in a conventional way. For example, directly
-        # use the basename of the lib file as lib name, e.g., cpp_info.libs = ["liblib1.a"]
-        # Issue related: https://github.com/conan-io/conan/issues/11331
-        if file_name in libs:  # let's ensure that it has any extension
-            lib_name = file_name
-        elif name in libs:
-            lib_name = name
-        elif name.startswith("lib"):
-            short_name = name[3:]  # libpkg -> pkg
-            if short_name in libs:
-                lib_name = short_name
-        # FIXME: CPS will be in charge of defining correctly this part. At the moment, we can
-        #        only guess the name of the DLL binary as it does not follow any Conan pattern.
-        if ext == "dll" or ext.endswith(".dll"):
-            if lib_name:
-                shared_windows_libs[lib_name] = formatted_path
-            elif total_libs_number == 1 and libs[0] not in shared_windows_libs:
-                shared_windows_libs[libs[0]] = formatted_path
-            else:  # let's cross the fingers... This is the last chance.
-                for lib in libs:
-                    if lib in name and lib not in shared_windows_libs:
-                        shared_windows_libs[lib] = formatted_path
-                        break
-        elif lib_name is not None:
-            lib_paths[lib_name] = formatted_path
-
-    cpp_info = cpp_info or dep.cpp_info
-    libs = cpp_info.libs[:]  # copying the values
-    if not libs:  # no libraries declared
-        return []
-    is_shared = _is_shared()
-    libdirs = cpp_info.libdirs
-    bindirs = cpp_info.bindirs if is_shared else []  # just want to get shared libraries
-    if hasattr(cpp_info, "aggregated_components"):
-        # Global cpp_info
-        total_libs_number = len(cpp_info.aggregated_components().libs)
-    else:
-        total_libs_number = len(libs)
-    lib_paths = {}
-    shared_windows_libs = {}
-    for libdir in set(libdirs + bindirs):
-        if not os.path.exists(libdir):
-            continue
-        files = os.listdir(libdir)
-        for f in files:
-            full_path = os.path.join(libdir, f)
-            if not os.path.isfile(full_path):  # Make sure that directories are excluded
-                continue
-            _, ext = os.path.splitext(f)
-            if is_shared and ext in (".so", ".dylib", ".lib", ".dll"):
-                _save_lib_path(f, full_path)
-            elif not is_shared and ext in (".a", ".lib"):
-                _save_lib_path(f, full_path)
-
-    libraries = []
-    for name, lib_path in lib_paths.items():
-        import_lib_path = None
-        if is_shared and os.path.splitext(lib_path)[1] == ".lib":
-            if name not in shared_windows_libs:
-                raise ConanException(f"Windows needs a .lib for link-time and .dll for runtime."
-                                     f" Only found {lib_path}")
-            import_lib_path = lib_path  # .lib
-            lib_path = shared_windows_libs.pop(name)  # .dll
-        libraries.append((name, is_shared, lib_path, import_lib_path))
-    # TODO: Would we want to manage the cases where DLLs are provided by the system?
-    return libraries
-
-
 def _get_headers(cpp_info, package_folder_path):
     return ['"{}/**"'.format(_relativize_path(path, package_folder_path))
             for path in cpp_info.includedirs]
@@ -342,12 +248,19 @@ class _BazelDependenciesBZLGenerator:
         save("BUILD.bazel", "# This is an empty BUILD file.")
 
 
+class _LibInfo:
+    def __init__(self, lib_name, cpp_info_, package_folder_path):
+        self.name = lib_name
+        self.is_shared = cpp_info_.type == PackageType.SHARED
+        self.lib_path = _relativize_path(cpp_info_.location, package_folder_path)
+        self.import_lib_path = _relativize_path(cpp_info_.link_location, package_folder_path)
+
+
 class _BazelBUILDGenerator:
     """
     This class creates the BUILD.bazel for each dependency where it's declared all the
     necessary information to load the libraries
     """
-
     # If both files exist, BUILD.bazel takes precedence over BUILD
     # https://bazel.build/concepts/build-files
     filename = "BUILD.bazel"
@@ -490,8 +403,38 @@ class _BazelBUILDGenerator:
         """
         return self._root_package_info.repository_name
 
+    def get_full_libs_info(self):
+        full_libs_info = defaultdict(list)
+        cpp_info = self._dep.cpp_info
+        deduced_cpp_info = cpp_info.deduce_full_cpp_info(self._dep)
+        package_folder_path = self.package_folder
+        # Root
+        if len(cpp_info.libs) > 1:
+            for lib_name in cpp_info.libs:
+                virtual_component = deduced_cpp_info.components.pop(f"_{lib_name}")  # removing it!
+                full_libs_info["root"].append(
+                    _LibInfo(lib_name, virtual_component, package_folder_path)
+                )
+        elif cpp_info.libs:
+            full_libs_info["root"].append(
+                _LibInfo(cpp_info.libs[0], deduced_cpp_info, package_folder_path)
+            )
+        # components
+        for cmp_name, cmp_cpp_info in deduced_cpp_info.components.items():
+            if cmp_name == "_common":  # FIXME: placeholder?
+                continue
+            if len(cmp_cpp_info.libs) > 1:
+                raise ConanException(f"BazelDeps only allows 1 lib per component:\n"
+                                     f"{self._dep}: {cmp_cpp_info.libs}")
+            elif cmp_cpp_info.libs:
+                # Bazel needs to relativize each path
+                full_libs_info[_get_component_name(self._dep, cmp_name)].append(
+                    _LibInfo(cmp_cpp_info.libs[0], cmp_cpp_info, package_folder_path)
+                )
+        return full_libs_info
+
     def _get_context(self):
-        def fill_info(info):
+        def fill_info(info, libs_info):
             ret = {
                 "name": info.name,  # package name and components name
                 "libs": {},
@@ -511,17 +454,8 @@ class _BazelBUILDGenerator:
                 defines = _get_defines(cpp_info)
                 os_build = self._dep.settings_build.get_safe("os")
                 linkopts = _get_linkopts(cpp_info, os_build)
-                libs = _get_libs(self._dep, cpp_info)
-                libs_info = []
                 bindirs = [_relativize_path(bindir, package_folder_path)
                            for bindir in cpp_info.bindirs]
-                for (lib, is_shared, lib_path, import_lib_path) in libs:
-                    # Bazel needs to relativize each path
-                    libs_info.append(
-                        _LibInfo(lib, is_shared,
-                                 _relativize_path(lib_path, package_folder_path),
-                                 _relativize_path(import_lib_path, package_folder_path))
-                    )
                 ret.update({
                     "libs": libs_info,
                     "bindirs": bindirs,
@@ -535,12 +469,14 @@ class _BazelBUILDGenerator:
 
         package_folder_path = self.package_folder
         context = dict()
-        context["root"] = fill_info(self._root_package_info)
+        full_libs_info = self.get_full_libs_info()
+        context["root"] = fill_info(self._root_package_info, full_libs_info.get("root", []))
         context["components"] = []
         for component in self._components_info:
-            component_context = fill_info(component)
+            component_context = fill_info(component, full_libs_info.get(component.name, []))
             context["components"].append(component_context)
             context["root"]["component_names"].append(component_context["name"])
+
         return context
 
     def generate(self):
diff --git a/conan/tools/microsoft/msbuild.py b/conan/tools/microsoft/msbuild.py
index a2ec898ca..e8efb2772 100644
--- a/conan/tools/microsoft/msbuild.py
+++ b/conan/tools/microsoft/msbuild.py
@@ -64,8 +64,8 @@ class MSBuild(object):
 
         maxcpucount = self._conanfile.conf.get("tools.microsoft.msbuild:max_cpu_count",
                                                check_type=int)
-        if maxcpucount:
-            cmd += " /m:{}".format(maxcpucount)
+        if maxcpucount is not None:
+            cmd += f" /m:{maxcpucount}" if maxcpucount > 0 else " /m"
 
         if targets:
             if not isinstance(targets, list):
diff --git a/conans/client/graph/build_mode.py b/conans/client/graph/build_mode.py
index 52d7e37c8..12b121e09 100644
--- a/conans/client/graph/build_mode.py
+++ b/conans/client/graph/build_mode.py
@@ -63,7 +63,7 @@ class BuildMode:
         # TODO: ref can be obtained from conan_file
 
         for pattern in self._excluded_patterns:
-            if ref_matches(ref, pattern, is_consumer=conan_file._conan_is_consumer):
+            if ref_matches(ref, pattern, is_consumer=conan_file._conan_is_consumer):  # noqa
                 conan_file.output.info("Excluded build from source")
                 return False
 
@@ -82,7 +82,7 @@ class BuildMode:
 
         # Patterns to match, if package matches pattern, build is forced
         for pattern in self.patterns:
-            if ref_matches(ref, pattern, is_consumer=conan_file._conan_is_consumer):
+            if ref_matches(ref, pattern, is_consumer=conan_file._conan_is_consumer):  # noqa
                 return True
         return False
 
@@ -109,7 +109,7 @@ class BuildMode:
             return True  # If it has not been excluded by the negated patterns, it is included
 
         for pattern in self._build_compatible_patterns:
-            if ref_matches(conanfile.ref, pattern, is_consumer=conanfile._conan_is_consumer):
+            if ref_matches(conanfile.ref, pattern, is_consumer=conanfile._conan_is_consumer):  # noqa
                 return True
 
     def should_build_missing(self, conanfile):
@@ -120,5 +120,5 @@ class BuildMode:
             return True  # If it has not been excluded by the negated patterns, it is included
 
         for pattern in self.build_missing_patterns:
-            if ref_matches(conanfile.ref, pattern, is_consumer=conanfile._conan_is_consumer):
+            if ref_matches(conanfile.ref, pattern, is_consumer=conanfile._conan_is_consumer):  # noqa
                 return True
diff --git a/conans/client/graph/graph_binaries.py b/conans/client/graph/graph_binaries.py
index db960e525..0a0714017 100644
--- a/conans/client/graph/graph_binaries.py
+++ b/conans/client/graph/graph_binaries.py
@@ -230,6 +230,10 @@ class GraphBinariesAnalyzer:
                                               "didn't enable 'tools.graph:vendor=build' to compute " \
                                               "its dependencies"
                 node.binary = BINARY_INVALID
+            if any(n.node.binary in (BINARY_EDITABLE, BINARY_EDITABLE_BUILD)
+                   for n in node.transitive_deps.values()):
+                conanfile.output.warning("Package is being built in the cache using editable "
+                                         "dependencies, this is dangerous", warn_tag="risk")
 
     def _process_node(self, node, build_mode, remotes, update):
         # Check that this same reference hasn't already been checked
diff --git a/conans/client/graph/graph_builder.py b/conans/client/graph/graph_builder.py
index 8745f8f07..eb08479bb 100644
--- a/conans/client/graph/graph_builder.py
+++ b/conans/client/graph/graph_builder.py
@@ -5,7 +5,7 @@ from collections import deque
 from conan.internal.cache.conan_reference_layout import BasicLayout
 from conan.internal.methods import run_configure_method
 from conans.client.graph.graph import DepsGraph, Node, CONTEXT_HOST, \
-    CONTEXT_BUILD, TransitiveRequirement, RECIPE_VIRTUAL, RECIPE_EDITABLE
+    CONTEXT_BUILD, TransitiveRequirement, RECIPE_VIRTUAL, RECIPE_EDITABLE, RECIPE_CONSUMER
 from conans.client.graph.graph import RECIPE_PLATFORM
 from conans.client.graph.graph_error import GraphLoopError, GraphConflictError, GraphMissingError, \
     GraphRuntimeError, GraphError
@@ -39,7 +39,9 @@ class DepsGraphBuilder(object):
         # print("Loading graph")
         dep_graph = DepsGraph()
 
-        self._prepare_node(root_node, profile_host, profile_build, Options())
+        is_test_package = getattr(root_node.conanfile, "tested_reference_str", None)
+        define_consumers = root_node.recipe == RECIPE_VIRTUAL or is_test_package
+        self._prepare_node(root_node, profile_host, profile_build, Options(), define_consumers)
         rs = self._initialize_requires(root_node, dep_graph, graph_lock, profile_build, profile_host)
         dep_graph.add_node(root_node)
 
@@ -171,8 +173,7 @@ class DepsGraphBuilder(object):
                 raise GraphConflictError(node, require, prev_node, prev_require, base_previous)
 
     @staticmethod
-    def _prepare_node(node, profile_host, profile_build, down_options):
-
+    def _prepare_node(node, profile_host, profile_build, down_options, define_consumers=False):
         # basic node configuration: calling configure() and requirements()
         conanfile, ref = node.conanfile, node.ref
 
@@ -180,10 +181,17 @@ class DepsGraphBuilder(object):
         assert isinstance(profile_options, Options), type(profile_options)
         run_configure_method(conanfile, down_options, profile_options, ref)
 
+        if define_consumers:  # Mark this requirements as the "consumers" nodes
+            tested_ref = getattr(conanfile, "tested_reference_str", None)
+            tested_ref = RecipeReference.loads(tested_ref) if tested_ref else None
+            for r in conanfile.requires.values():
+                if tested_ref is None or r.ref == tested_ref:
+                    r.is_consumer = True
+
         # Apply build_tools_requires from profile, overriding the declared ones
         profile = profile_host if node.context == CONTEXT_HOST else profile_build
         for pattern, tool_requires in profile.tool_requires.items():
-            if ref_matches(ref, pattern, is_consumer=conanfile._conan_is_consumer):
+            if ref_matches(ref, pattern, is_consumer=conanfile._conan_is_consumer):  # noqa
                 for tool_require in tool_requires:  # Do the override
                     if str(tool_require) == str(ref):  # FIXME: Ugly str comparison
                         continue  # avoid self-loop of build-requires in build context
@@ -364,9 +372,7 @@ class DepsGraphBuilder(object):
 
         new_ref = layout.reference
         dep_conanfile.folders.set_base_recipe_metadata(layout.metadata())  # None for platform_xxx
-        # If the node is virtual or a test package, the require is also "root"
-        is_test_package = getattr(node.conanfile, "tested_reference_str", False)
-        if node.conanfile._conan_is_consumer and (node.recipe == RECIPE_VIRTUAL or is_test_package):
+        if getattr(require, "is_consumer", None):
             dep_conanfile._conan_is_consumer = True
         initialize_conanfile_profile(dep_conanfile, profile_build, profile_host, node.context,
                                      require.build, new_ref, parent=node.conanfile)
diff --git a/conans/client/graph/profile_node_definer.py b/conans/client/graph/profile_node_definer.py
index fb4c4a4a4..1b5bba25b 100644
--- a/conans/client/graph/profile_node_definer.py
+++ b/conans/client/graph/profile_node_definer.py
@@ -31,8 +31,9 @@ def initialize_conanfile_profile(conanfile, profile_build, profile_host, base_co
     else:
         _initialize_conanfile(conanfile, profile_host, settings_host, ref)
         # Host profile with some build profile information
-        conanfile.buildenv_build = profile_build.buildenv.get_profile_env(ref, conanfile._conan_is_consumer)
-        conanfile.conf_build = profile_build.conf.get_conanfile_conf(ref, conanfile._conan_is_consumer)
+        is_consumer = conanfile._conan_is_consumer  # noqa
+        conanfile.buildenv_build = profile_build.buildenv.get_profile_env(ref, is_consumer)
+        conanfile.conf_build = profile_build.conf.get_conanfile_conf(ref, is_consumer)
     conanfile.settings_build = settings_build
     conanfile.settings_target = None
 
@@ -60,7 +61,7 @@ def _per_package_settings(conanfile, profile, ref):
         pkg_settings = []
 
         for pattern, settings in package_settings_values.items():
-            if ref_matches(ref, pattern, conanfile._conan_is_consumer):
+            if ref_matches(ref, pattern, conanfile._conan_is_consumer):  # noqa
                 pkg_settings.extend(settings)
 
         if pkg_settings:
@@ -80,7 +81,8 @@ def _initialize_conanfile(conanfile, profile, settings, ref):
     conanfile.settings._frozen = True
     conanfile._conan_buildenv = profile.buildenv
     conanfile._conan_runenv = profile.runenv
-    conanfile.conf = profile.conf.get_conanfile_conf(ref, conanfile._conan_is_consumer)  # Maybe this can be done lazy too
+    # Maybe this can be done lazy too
+    conanfile.conf = profile.conf.get_conanfile_conf(ref, conanfile._conan_is_consumer)  # noqa
 
 
 def consumer_definer(conanfile, profile_host, profile_build):
diff --git a/conans/client/loader.py b/conans/client/loader.py
index ecad28572..8fb865f75 100644
--- a/conans/client/loader.py
+++ b/conans/client/loader.py
@@ -171,11 +171,8 @@ class ConanFileLoader:
         else:
             conanfile.display_name = os.path.basename(conanfile_path)
         conanfile.output.scope = conanfile.display_name
-        try:
-            conanfile._conan_is_consumer = True
-            return conanfile
-        except Exception as e:  # re-raise with file name
-            raise ConanException("%s: %s" % (conanfile_path, str(e)))
+        conanfile._conan_is_consumer = True
+        return conanfile
 
     def load_conanfile(self, conanfile_path, ref, graph_lock=None, remotes=None,
                        update=None, check_update=None):
@@ -396,7 +393,7 @@ def _get_required_conan_version_without_loading(conan_file_path):
         found = re.search(r"(.*)required_conan_version\s*=\s*[\"'](.*)[\"']", contents)
         if found and "#" not in found.group(1):
             txt_version = found.group(2)
-    except:
+    except:  # noqa this should be solid, cannot fail
         pass
 
     return txt_version
diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index ca4d00e36..dc8e79a6c 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -2,6 +2,7 @@ import copy
 import glob
 import json
 import os
+import re
 from collections import OrderedDict, defaultdict
 
 from conan.api.output import ConanOutput
@@ -445,7 +446,12 @@ class _Component:
 
         if other._properties:
             current_values = self.get_init("_properties", {})
-            current_values.update(other._properties)
+            for k, v in other._properties.items():
+                existing = current_values.get(k)
+                if existing is not None and isinstance(existing, list) and not overwrite:
+                    existing.extend(v)
+                else:
+                    current_values[k] = v
 
     def set_relative_base_folder(self, folder):
         for varname in _DIRS_VAR_NAMES:
@@ -481,46 +487,78 @@ class _Component:
     def parsed_requires(self):
         return [r.split("::", 1) if "::" in r else (None, r) for r in self.requires]
 
-    def deduce_locations(self, conanfile):
-        pkg_type = conanfile.package_type
-        if self._exe:   # exe is a new field, it should have the correct location
-            return
-        if self._location or self._link_location:
-            if self._type is None or self._type is PackageType.HEADER:
-                raise ConanException("Incorrect cpp_info defining location without type or header")
-            return
-        if self._type not in [None, PackageType.SHARED, PackageType.STATIC, PackageType.APP]:
-            return
-
-        if len(self.libs) == 0:
-            return
-
-        if len(self.libs) != 1:
-            raise ConanException("More than 1 library defined in cpp_info.libs, cannot deduce CPS")
-
-        # Recipe didn't specify things, need to auto deduce
-        libdirs = [x.replace("\\", "/") for x in self.libdirs]
-        bindirs = [x.replace("\\", "/") for x in self.bindirs]
+    def _auto_deduce_locations(self, conanfile, component_name):
+
+        def _lib_match_by_glob(dir_, filename):
+            # Run a glob.glob function to find the file given by the filename
+            matches = glob.glob(f"{dir_}/{filename}")
+            if matches:
+                return matches
+
+
+        def _lib_match_by_regex(dir_, pattern):
+            ret = set()
+            # pattern is a regex compiled pattern, so let's iterate each file to find the library
+            files = os.listdir(dir_)
+            for file_name in files:
+                full_path = os.path.join(dir_, file_name)
+                if os.path.isfile(full_path) and pattern.match(file_name):
+                    # File name could be a symlink, e.g., mylib.1.0.0.so -> mylib.so
+                    if os.path.islink(full_path):
+                        # Important! os.path.realpath returns the final path of the symlink even if it points
+                        # to another symlink, i.e., libmylib.dylib -> libmylib.1.dylib -> libmylib.1.0.0.dylib
+                        # then os.path.realpath("libmylib.1.0.0.dylib") == "libmylib.dylib"
+                        # Note: os.readlink() returns the path which the symbolic link points to.
+                        real_path = os.path.realpath(full_path)
+                        if pattern.match(os.path.basename(real_path)):
+                            ret.add(real_path)
+                    else:
+                        ret.add(full_path)
+            return list(ret)
+
+
+        def _find_matching(dirs, pattern):
+            for d in dirs:
+                if not os.path.exists(d):
+                    continue
+                # If pattern is an exact match
+                if isinstance(pattern, str):
+                    # pattern == filename
+                    lib_found = _lib_match_by_glob(d, pattern)
+                else:
+                    lib_found = _lib_match_by_regex(d, pattern)
+                if lib_found:
+                    if len(lib_found) > 1:
+                        lib_found.sort()
+                        out.warning(f"There were several matches for Lib {libname}: {lib_found}")
+                    return lib_found[0].replace("\\", "/")
 
-        # TODO: Do a better handling of pre-defined type
-        libname = self.libs[0]
-        static_patterns = [f"{libname}.lib", f"{libname}.a", f"lib{libname}.a"]
-        shared_patterns = [f"lib{libname}.so", f"lib{libname}.so.*", f"lib{libname}.dylib",
-                           f"lib{libname}.*dylib"]
-        dll_patterns = [f"{libname}.dll"]
-
-        def _find_matching(patterns, dirs):
-            matches = set()
-            for pattern in patterns:
-                for d in dirs:
-                    matches.update(glob.glob(f"{d}/{pattern}"))
-            if len(matches) == 1:
-                return next(iter(matches))
-
-        static_location = _find_matching(static_patterns, libdirs)
-        shared_location = _find_matching(shared_patterns, libdirs)
-        dll_location = _find_matching(dll_patterns, bindirs)
         out = ConanOutput(scope=str(conanfile))
+        pkg_type = conanfile.package_type
+        libdirs = self.libdirs
+        bindirs = self.bindirs
+        libname = self.libs[0]
+        static_location = None
+        shared_location = None
+        dll_location = None
+        # libname is exactly the pattern, e.g., ["mylib.a"] instead of ["mylib"]
+        _, ext = os.path.splitext(libname)
+        if ext in (".lib", ".a", ".dll", ".so", ".dylib"):
+            if ext in (".lib", ".a"):
+                static_location = _find_matching(libdirs, libname)
+            elif ext in (".so", ".dylib"):
+                shared_location = _find_matching(libdirs, libname)
+            elif ext == ".dll":
+                dll_location = _find_matching(bindirs, libname)
+        else:
+            regex_static = re.compile(rf"(?:lib)?{libname}(?:[._-].+)?\.(?:a|lib)")
+            regex_shared = re.compile(rf"(?:lib)?{libname}(?:[._-].+)?\.(?:so|dylib)")
+            regex_dll = re.compile(rf"(?:.+)?({libname}|{component_name})(?:.+)?\.dll")
+            static_location = _find_matching(libdirs, regex_static)
+            shared_location = _find_matching(libdirs, regex_shared)
+            if static_location or not shared_location:
+                dll_location = _find_matching(bindirs, regex_dll)
+
         if static_location:
             if shared_location:
                 out.warning(f"Lib {libname} has both static {static_location} and "
@@ -553,6 +591,25 @@ class _Component:
         if self._type != pkg_type:
             out.warning(f"Lib {libname} deduced as '{self._type}, but 'package_type={pkg_type}'")
 
+    def deduce_locations(self, conanfile, component_name=""):
+        if self._exe:   # exe is a new field, it should have the correct location
+            return
+        if self._location or self._link_location:
+            if self._type is None or self._type is PackageType.HEADER:
+                raise ConanException("Incorrect cpp_info defining location without type or header")
+            return
+        if self._type not in [None, PackageType.SHARED, PackageType.STATIC, PackageType.APP]:
+            return
+        num_libs = len(self.libs)
+        if num_libs == 0:
+            return
+        elif num_libs > 1:
+            raise ConanException(
+                f"More than 1 library defined in cpp_info.libs, cannot deduce CPS ({num_libs} libraries found)")
+        else:
+            # If no location is defined, it's time to guess the location
+            self._auto_deduce_locations(conanfile, component_name=component_name)
+
 
 class CppInfo:
 
@@ -734,6 +791,7 @@ class CppInfo:
                 c.type = self.type  # This should be a string
                 c.includedirs = self.includedirs
                 c.libdirs = self.libdirs
+                c.bindirs = self.bindirs
                 c.libs = [lib]
                 result.components[f"_{lib}"] = c
 
@@ -741,15 +799,14 @@ class CppInfo:
             common.libs = []
             common.type = str(PackageType.HEADER)  # the type of components is a string!
             common.requires = list(result.components.keys()) + (self.requires or [])
-
             result.components["_common"] = common
         else:
             result._package = self._package.clone()
             result.default_components = self.default_components
             result.components = {k: v.clone() for k, v in self.components.items()}
 
-        result._package.deduce_locations(conanfile)
-        for comp in result.components.values():
-            comp.deduce_locations(conanfile)
+        result._package.deduce_locations(conanfile, component_name=conanfile.ref.name)
+        for comp_name, comp in result.components.items():
+            comp.deduce_locations(conanfile, component_name=comp_name)
 
         return result
diff --git a/conans/model/dependencies.py b/conans/model/dependencies.py
index 44a52125b..82e9a0e95 100644
--- a/conans/model/dependencies.py
+++ b/conans/model/dependencies.py
@@ -91,12 +91,16 @@ class ConanFileDependencies(UserRequirementsDict):
     def from_node(node):
         d = OrderedDict((require, ConanFileInterface(transitive.node.conanfile))
                         for require, transitive in node.transitive_deps.items())
-        for old_req, new_req in node.replaced_requires.items():
-            existing = d.pop(new_req, None)
-            if existing is not None:
+        if node.replaced_requires:
+            for old_req, new_req in node.replaced_requires.items():
+                # Two different replaced_requires can point to the same real requirement
+                existing = d[new_req]
                 added_req = new_req.copy_requirement()
                 added_req.ref = RecipeReference.loads(old_req)
                 d[added_req] = existing
+            # Now remove the replaced from dependencies dict
+            for new_req in node.replaced_requires.values():
+                d.pop(new_req, None)
         return ConanFileDependencies(d)
 
     def filter(self, require_filter, remove_system=True):
diff --git a/setup_server.py b/setup_server.py
index 4da637264..8a6254b73 100644
--- a/setup_server.py
+++ b/setup_server.py
@@ -52,7 +52,7 @@ dev_requirements = get_requires("conans/requirements_dev.txt")
 
 
 setup(
-    name='conan-server',
+    name='conan_server',
     # Versions should comply with PEP440.  For a discussion on single-sourcing
     # the version across setup.py and the project code, see
     # https://packaging.python.org/en/latest/single_source_version.html


Test patch:
diff --git a/conan/test/assets/genconanfile.py b/conan/test/assets/genconanfile.py
index 717b12b27..55f61de54 100644
--- a/conan/test/assets/genconanfile.py
+++ b/conan/test/assets/genconanfile.py
@@ -227,8 +227,8 @@ class GenConanfile(object):
         return self
 
     def with_package_info(self, cpp_info=None, env_info=None):
-        assert isinstance(cpp_info, dict), "cpp_info ({}) expects dict".format(type(cpp_info))
-        assert isinstance(env_info, dict), "env_info ({}) expects dict".format(type(env_info))
+        assert cpp_info is None or isinstance(cpp_info, dict), "cpp_info ({}) expects dict".format(type(cpp_info))
+        assert env_info is None or isinstance(env_info, dict), "env_info ({}) expects dict".format(type(env_info))
         self._package_info = self._package_info or {}
         if cpp_info:
             self._package_info["cpp_info"] = cpp_info
diff --git a/conan/test/utils/mocks.py b/conan/test/utils/mocks.py
index acdb379c0..3e616b70a 100644
--- a/conan/test/utils/mocks.py
+++ b/conan/test/utils/mocks.py
@@ -2,8 +2,8 @@ from collections import defaultdict
 from io import StringIO
 
 from conan import ConanFile
-from conan.internal.conan_app import ConanFileHelpers
 from conan.errors import ConanException
+from conan.internal.conan_app import ConanFileHelpers
 from conans.model.conf import Conf
 from conans.model.layout import Folders, Infos
 from conans.model.options import Options
diff --git a/conan/test/utils/tools.py b/conan/test/utils/tools.py
index a84187934..da021b2d8 100644
--- a/conan/test/utils/tools.py
+++ b/conan/test/utils/tools.py
@@ -466,6 +466,12 @@ class TestClient:
     def load(self, filename):
         return load(os.path.join(self.current_folder, filename))
 
+    def load_home(self, filename):
+        try:
+            return load(os.path.join(self.cache_folder, filename))
+        except IOError:
+            return None
+
     @property
     def cache(self):
         # Returns a temporary cache object intended for inspecting it
@@ -849,115 +855,6 @@ class TestClient:
         return self.cache.pkg_layout(pref)
 
 
-class TurboTestClient(TestClient):
-
-    def __init__(self, *args, **kwargs):
-        super(TurboTestClient, self).__init__(*args, **kwargs)
-
-    def create(self, ref, conanfile=GenConanfile(), args=None, assert_error=False):
-        if conanfile:
-            self.save({"conanfile.py": conanfile})
-        full_str = f"--name={ref.name} --version={ref.version}"
-        if ref.user:
-            full_str += f" --user={ref.user}"
-        if ref.channel:
-            full_str += f" --channel={ref.channel}"
-        self.run("create . {} {}".format(full_str, args or ""),
-                 assert_error=assert_error)
-
-        tmp = copy.copy(ref)
-        tmp.revision = None
-        ref = self.cache.get_latest_recipe_reference(tmp)
-
-        if assert_error:
-            return None
-
-        package_id = self.created_package_id(ref)
-        package_ref = PkgReference(ref, package_id)
-        tmp = copy.copy(package_ref)
-        tmp.revision = None
-        prevs = self.cache.get_package_revisions_references(tmp, only_latest_prev=True)
-        prev = prevs[0]
-
-        return prev
-
-    def upload_all(self, ref, remote=None, args=None, assert_error=False):
-        remote = remote or list(self.servers.keys())[0]
-        self.run("upload {} -c -r {} {}".format(ref.repr_notime(), remote, args or ""),
-                 assert_error=assert_error)
-        if not assert_error:
-            remote_rrev, _ = self.servers[remote].server_store.get_last_revision(ref)
-            _tmp = copy.copy(ref)
-            _tmp.revision = remote_rrev
-            return _tmp
-
-    def export_pkg(self, ref, conanfile=GenConanfile(), args=None, assert_error=False):
-        if conanfile:
-            self.save({"conanfile.py": conanfile})
-        self.run("export-pkg . {} {}".format(repr(ref),  args or ""),
-                 assert_error=assert_error)
-        # FIXME: What is this line? rrev is not used, is it checking existance or something?
-        rrev = self.cache.get_latest_recipe_reference(ref)
-
-        if assert_error:
-            return None
-        package_id = re.search(r"{}:(\S+)".format(str(ref)), str(self.out)).group(1)
-        package_ref = PkgReference(ref, package_id)
-        prev = self.cache.get_latest_package_reference(package_ref)
-        _tmp = copy.copy(package_ref)
-        _tmp.revision = prev
-        return _tmp
-
-    def recipe_revision(self, ref):
-        tmp = copy.copy(ref)
-        tmp.revision = None
-        latest_rrev = self.cache.get_latest_recipe_reference(tmp)
-        return latest_rrev.revision
-
-    def package_revision(self, pref):
-        tmp = copy.copy(pref)
-        tmp.revision = None
-        latest_prev = self.cache.get_latest_package_reference(tmp)
-        return latest_prev.revision
-
-    # FIXME: 2.0: adapt this function to using the new "conan list xxxx" and recover the xfail tests
-    def search(self, pattern, remote=None, assert_error=False, args=None):
-        remote = " -r={}".format(remote) if remote else ""
-        self.run("search {} --json {} {} {}".format(pattern, ".tmp.json", remote,
-                                                    args or ""),
-                 assert_error=assert_error)
-        data = json.loads(self.load(".tmp.json"))
-        return data
-
-    def massive_uploader(self, ref, revisions, num_prev, remote=None):
-        """Uploads N revisions with M package revisions. The revisions can be specified like:
-            revisions = [{"os": "Windows"}, {"os": "Linux"}], \
-                        [{"os": "Macos"}], \
-                        [{"os": "Solaris"}, {"os": "FreeBSD"}]
-
-            IMPORTANT: Different settings keys will cause different recipe revisions
-        """
-        remote = remote or "default"
-        ret = []
-        for i, settings_groups in enumerate(revisions):
-            tmp = []
-            for settings in settings_groups:
-                conanfile_gen = GenConanfile(). \
-                    with_build_msg("REV{}".format(i)). \
-                    with_package_file("file", env_var="MY_VAR")
-                for s in settings.keys():
-                    conanfile_gen = conanfile_gen.with_setting(s)
-                for k in range(num_prev):
-                    args = " ".join(["-s {}={}".format(key, value)
-                                     for key, value in settings.items()])
-                    with environment_update({"MY_VAR": str(k)}):
-                        pref = self.create(ref, conanfile=conanfile_gen, args=args)
-                        self.upload_all(ref, remote=remote)
-                        tmp.append(pref)
-                ret.append(tmp)
-        return ret
-
-
 def get_free_port():
     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     sock.bind(('localhost', 0))
diff --git a/test/functional/command/config_install_test.py b/test/functional/command/config_install_test.py
index 3a2575052..3c9912c76 100644
--- a/test/functional/command/config_install_test.py
+++ b/test/functional/command/config_install_test.py
@@ -758,3 +758,181 @@ class TestConfigInstallPkg:
         assert "Copying file global.conf" in c.out
         c.run("config show *")
         assert "user.myteam:myconf: myvalue" in c.out
+
+
+class TestConfigInstallPkgSettings:
+    conanfile = textwrap.dedent("""
+        import os
+        from conan import ConanFile
+        from conan.tools.files import copy
+        class Conf(ConanFile):
+            name = "myconf"
+            version = "0.1"
+            settings = "os"
+            package_type = "configuration"
+            def package(self):
+                f = "win" if self.settings.os == "Windows" else "nix"
+                copy(self, "*.conf", src=os.path.join(self.build_folder, f), dst=self.package_folder)
+            """)
+
+    @pytest.fixture()
+    def client(self):
+        c = TestClient(default_server_user=True)
+        c.save({"conanfile.py": self.conanfile,
+                "win/global.conf": "user.myteam:myconf=mywinvalue",
+                "nix/global.conf": "user.myteam:myconf=mynixvalue",
+                })
+        c.run("export-pkg . -s os=Windows")
+        c.run("export-pkg . -s os=Linux")
+        c.run("upload * -r=default -c")
+        c.run("remove * -c")
+        return c
+
+    @pytest.mark.parametrize("default_profile", [False, True])
+    def test_config_install_from_pkg(self, client, default_profile):
+        # Now install it
+        c = client
+        if not default_profile:
+            os.remove(os.path.join(c.cache_folder, "profiles", "default"))
+        c.run("config install-pkg myconf/[*] -s os=Windows")
+        assert "myconf/0.1: Downloaded package revision" in c.out
+        assert "Copying file global.conf" in c.out
+        c.run("config show *")
+        assert "user.myteam:myconf: mywinvalue" in c.out
+
+        c.run("config install-pkg myconf/[*] -s os=Linux")
+        assert "myconf/0.1: Downloaded package revision" in c.out
+        assert "Copying file global.conf" in c.out
+        c.run("config show *")
+        assert "user.myteam:myconf: mynixvalue" in c.out
+
+    def test_error_no_settings_defined(self, client):
+        c = client
+        os.remove(os.path.join(c.cache_folder, "profiles", "default"))
+        c.run("config install-pkg myconf/[*]", assert_error=True)
+        assert "There are invalid packages:" in c.out
+        assert "myconf/0.1: Invalid: 'settings.os' value not defined" in c.out
+
+    def test_config_install_from_pkg_profile(self, client):
+        # Now install it
+        c = client
+        c.save({"win.profile": "[settings]\nos=Windows",
+                "nix.profile": "[settings]\nos=Linux"})
+        c.run("config install-pkg myconf/[*] -pr=win.profile")
+        assert "myconf/0.1: Downloaded package revision" in c.out
+        assert "Copying file global.conf" in c.out
+        c.run("config show *")
+        assert "user.myteam:myconf: mywinvalue" in c.out
+
+        c.run("config install-pkg myconf/[*] -pr=nix.profile")
+        assert "myconf/0.1: Downloaded package revision" in c.out
+        assert "Copying file global.conf" in c.out
+        c.run("config show *")
+        assert "user.myteam:myconf: mynixvalue" in c.out
+
+    def test_config_install_from_pkg_profile_default(self, client):
+        # Now install it
+        c = client
+        c.save_home({"profiles/default": "[settings]\nos=Windows"})
+        c.run("config install-pkg myconf/[*]")
+        assert "myconf/0.1: Downloaded package revision" in c.out
+        assert "Copying file global.conf" in c.out
+        c.run("config show *")
+        assert "user.myteam:myconf: mywinvalue" in c.out
+
+        c.save_home({"profiles/default": "[settings]\nos=Linux"})
+        c.run("config install-pkg myconf/[*]")
+        assert "myconf/0.1: Downloaded package revision" in c.out
+        assert "Copying file global.conf" in c.out
+        c.run("config show *")
+        assert "user.myteam:myconf: mynixvalue" in c.out
+
+
+class TestConfigInstallPkgOptions:
+    conanfile = textwrap.dedent("""
+        import os
+        from conan import ConanFile
+        from conan.tools.files import copy
+        class Conf(ConanFile):
+            name = "myconf"
+            version = "0.1"
+            options = {"project": ["project1", "project2"]}
+            default_options = {"project": "project1"}
+            package_type = "configuration"
+            def package(self):
+                copy(self, "*.conf", src=os.path.join(self.build_folder, str(self.options.project)),
+                     dst=self.package_folder)
+            """)
+
+    @pytest.fixture()
+    def client(self):
+        c = TestClient(default_server_user=True)
+        c.save({"conanfile.py": self.conanfile,
+                "project1/global.conf": "user.myteam:myconf=my1value",
+                "project2/global.conf": "user.myteam:myconf=my2value",
+                })
+        c.run("export-pkg .")
+        c.run("export-pkg . -o project=project2")
+        c.run("upload * -r=default -c")
+        c.run("remove * -c")
+        return c
+
+    @pytest.mark.parametrize("default_profile", [False, True])
+    def test_config_install_from_pkg(self, client, default_profile):
+        # Now install it
+        c = client
+        if not default_profile:
+            os.remove(os.path.join(c.cache_folder, "profiles", "default"))
+        c.run("config install-pkg myconf/[*] -o &:project=project1")
+        assert "myconf/0.1: Downloaded package revision" in c.out
+        assert "Copying file global.conf" in c.out
+        c.run("config show *")
+        assert "user.myteam:myconf: my1value" in c.out
+
+        c.run("config install-pkg myconf/[*] -o &:project=project2")
+        assert "myconf/0.1: Downloaded package revision" in c.out
+        assert "Copying file global.conf" in c.out
+        c.run("config show *")
+        assert "user.myteam:myconf: my2value" in c.out
+
+    def test_no_option_defined(self, client):
+        c = client
+        os.remove(os.path.join(c.cache_folder, "profiles", "default"))
+        c.run("config install-pkg myconf/[*]")
+        assert "Copying file global.conf" in c.out
+        c.run("config show *")
+        assert "user.myteam:myconf: my1value" in c.out
+
+    def test_config_install_from_pkg_profile(self, client):
+        # Now install it
+        c = client
+        c.save({"win.profile": "[options]\n&:project=project1",
+                "nix.profile": "[options]\n&:project=project2"})
+        c.run("config install-pkg myconf/[*] -pr=win.profile")
+        assert "myconf/0.1: Downloaded package revision" in c.out
+        assert "Copying file global.conf" in c.out
+        c.run("config show *")
+        assert "user.myteam:myconf: my1value" in c.out
+
+        c.run("config install-pkg myconf/[*] -pr=nix.profile")
+        assert "myconf/0.1: Downloaded package revision" in c.out
+        assert "Copying file global.conf" in c.out
+        c.run("config show *")
+        assert "user.myteam:myconf: my2value" in c.out
+
+    def test_config_install_from_pkg_profile_default(self, client):
+        # Now install it
+        c = client
+        c.save_home({"profiles/default": "[options]\n&:project=project1"})
+        c.run("config install-pkg myconf/[*]")
+        assert "myconf/0.1: Downloaded package revision" in c.out
+        assert "Copying file global.conf" in c.out
+        c.run("config show *")
+        assert "user.myteam:myconf: my1value" in c.out
+
+        c.save_home({"profiles/default": "[options]\n&:project=project2"})
+        c.run("config install-pkg myconf/[*]")
+        assert "myconf/0.1: Downloaded package revision" in c.out
+        assert "Copying file global.conf" in c.out
+        c.run("config show *")
+        assert "user.myteam:myconf: my2value" in c.out
diff --git a/test/functional/layout/test_build_system_layout_helpers.py b/test/functional/layout/test_build_system_layout_helpers.py
index 7caaaae26..a3ba56a48 100644
--- a/test/functional/layout/test_build_system_layout_helpers.py
+++ b/test/functional/layout/test_build_system_layout_helpers.py
@@ -4,9 +4,8 @@ import textwrap
 
 import pytest
 
-from conans.model.recipe_ref import RecipeReference
 from conan.test.assets.genconanfile import GenConanfile
-from conan.test.utils.tools import TestClient, TurboTestClient
+from conan.test.utils.tools import TestClient
 from conans.util.files import save, load
 
 
diff --git a/test/functional/revisions_test.py b/test/functional/revisions_test.py
index 35387d956..d7dd9c370 100644
--- a/test/functional/revisions_test.py
+++ b/test/functional/revisions_test.py
@@ -10,10 +10,21 @@ from conan.test.utils.env import environment_update
 from conan.internal.errors import RecipeNotFoundException
 from conans.model.recipe_ref import RecipeReference
 from conans.server.revision_list import RevisionList
-from conan.test.utils.tools import TestServer, TurboTestClient, GenConanfile, TestClient
+from conan.test.utils.tools import TestServer, GenConanfile, TestClient
 from conans.util.files import load
 
 
+def _create(c_v2, ref, conanfile=None, args=None, assert_error=False):
+    conanfile = conanfile or GenConanfile()
+    c_v2.save({"conanfile.py": str(conanfile)})
+    r = ref
+    c_v2.run(f"create . --name {r.name} --version {r.version} "
+             f"--user {r.user} --channel {r.channel} {args or ''}", assert_error=assert_error)
+    if not assert_error:
+        pref = c_v2.created_package_reference(str(ref))
+        return pref
+
+
 @pytest.mark.artifactory_ready
 class InstallingPackagesWithRevisionsTest(unittest.TestCase):
 
@@ -22,27 +33,39 @@ class InstallingPackagesWithRevisionsTest(unittest.TestCase):
         self.server2 = TestServer()
         self.servers = OrderedDict([("default", self.server),
                                     ("remote2", self.server2)])
-        self.c_v2 = TurboTestClient(servers=self.servers, inputs=2*["admin", "password"])
+        self.c_v2 = TestClient(servers=self.servers, inputs=2*["admin", "password"])
         self.ref = RecipeReference.loads("lib/1.0@conan/testing")
 
+    def recipe_revision(self, ref):
+        tmp = copy.copy(ref)
+        tmp.revision = None
+        latest_rrev = self.c_v2.cache.get_latest_recipe_reference(tmp)
+        return latest_rrev.revision
+
+    def package_revision(self, pref):
+        tmp = copy.copy(pref)
+        tmp.revision = None
+        latest_prev = self.c_v2.cache.get_latest_package_reference(tmp)
+        return latest_prev.revision
+
     def test_install_binary_iterating_remotes_same_rrev(self):
         """We have two servers (remote1 and remote2), first with a recipe but the
         second one with a PREV of the binary.
         If a client installs without specifying -r remote1, it will iterate remote2 also"""
         conanfile = GenConanfile().with_package_file("file.txt", env_var="MY_VAR")
         with environment_update({"MY_VAR": "1"}):
-            pref = self.c_v2.create(self.ref, conanfile=conanfile)
+            pref = _create(self.c_v2, self.ref, conanfile=conanfile)
         the_time = time.time()
         with patch.object(RevisionList, '_now', return_value=the_time):
-            self.c_v2.upload_all(self.ref, remote="default")
+            self.c_v2.run(f"upload {self.ref} -r=default -c")
         self.c_v2.run("remove {}#*:{} -c -r default".format(self.ref, pref.package_id))
         # Same RREV, different PREV
         with environment_update({"MY_VAR": "2"}):
-            pref2 = self.c_v2.create(self.ref, conanfile=conanfile)
+            pref2 = _create(self.c_v2, self.ref, conanfile=conanfile)
 
         the_time = the_time + 10.0
         with patch.object(RevisionList, '_now', return_value=the_time):
-            self.c_v2.upload_all(self.ref, remote="remote2")
+            self.c_v2.run(f"upload {self.ref} -r=remote2 -c")
         self.c_v2.remove_all()
 
         self.assertEqual(pref.ref.revision, pref2.ref.revision)
@@ -58,29 +81,29 @@ class InstallingPackagesWithRevisionsTest(unittest.TestCase):
 
         # Two revisions of "lib1" to the server
         lib1 = RecipeReference.loads("lib1/1.0@conan/stable")
-        lib1_pref = self.c_v2.create(lib1)
-        self.c_v2.upload_all(lib1)
-        lib1b_pref = self.c_v2.create(lib1, conanfile=GenConanfile().with_build_msg("Rev2"))
-        self.c_v2.upload_all(lib1)
+        lib1_pref = _create(self.c_v2, lib1)
+        self.c_v2.run(f"upload {lib1} -r=default -c")
+        lib1b_pref = _create(self.c_v2, lib1, conanfile=GenConanfile().with_build_msg("Rev2"))
+        self.c_v2.run(f"upload {lib1} -r=default -c")
 
         # Lib2 depending of lib1
         self.c_v2.remove_all()
         lib2 = RecipeReference.loads("lib2/1.0@conan/stable")
-        self.c_v2.create(lib2, conanfile=GenConanfile().with_requirement(lib1_pref.ref))
-        self.c_v2.upload_all(lib2)
+        _create(self.c_v2, lib2, conanfile=GenConanfile().with_requirement(lib1_pref.ref))
+        self.c_v2.run(f"upload {lib2} -r=default -c")
 
         # Lib3 depending of lib1b
         self.c_v2.remove_all()
         lib3 = RecipeReference.loads("lib3/1.0@conan/stable")
-        self.c_v2.create(lib3, conanfile=GenConanfile().with_requirement(lib1b_pref.ref))
-        self.c_v2.upload_all(lib3)
+        _create(self.c_v2, lib3, conanfile=GenConanfile().with_requirement(lib1b_pref.ref))
+        self.c_v2.run(f"upload {lib3} -r=default -c")
 
         # Project depending on both lib3 and lib2
         self.c_v2.remove_all()
         project = RecipeReference.loads("project/1.0@conan/stable")
-        self.c_v2.create(project,
-                         conanfile=GenConanfile().with_requirement(lib2).with_requirement(lib3),
-                         assert_error=True)
+        _create(self.c_v2, project,
+                conanfile=GenConanfile().with_requirement(lib2).with_requirement(lib3),
+                assert_error=True)
         self.assertIn("ERROR: Version conflict", self.c_v2.out)
         # self.assertIn("Different revisions of {} has been requested".format(lib1), self.c_v2.out)
 
@@ -88,12 +111,12 @@ class InstallingPackagesWithRevisionsTest(unittest.TestCase):
         """ If an alias points to a RREV, it resolved that RREV and no other"""
 
         # Upload one revision
-        pref = self.c_v2.create(self.ref)
-        self.c_v2.upload_all(self.ref)
+        pref = _create(self.c_v2, self.ref)
+        self.c_v2.run(f"upload {self.ref} -r=default -c")
 
         # Upload other revision
-        self.c_v2.create(self.ref, conanfile=GenConanfile().with_build_msg("Build Rev 2"))
-        self.c_v2.upload_all(self.ref)
+        _create(self.c_v2, self.ref, conanfile=GenConanfile().with_build_msg("Build Rev 2"))
+        self.c_v2.run(f"upload {self.ref} -r=default -c")
         self.c_v2.remove_all()
 
         # Create an alias to the first revision
@@ -102,7 +125,7 @@ class InstallingPackagesWithRevisionsTest(unittest.TestCase):
         exported = load(self.c_v2.get_latest_ref_layout(alias_ref).conanfile())
         self.assertIn('alias = "{}"'.format(repr(pref.ref)), exported)
 
-        self.c_v2.upload_all(RecipeReference.loads("lib/latest@conan/stable"))
+        self.c_v2.run(f"upload lib/latest@conan/stable -r=default -c")
         self.c_v2.remove_all()
 
         self.c_v2.run("install --requires=lib/(latest)@conan/stable")
@@ -110,22 +133,22 @@ class InstallingPackagesWithRevisionsTest(unittest.TestCase):
         self.assertNotIn("doesn't belong to the installed recipe revision", self.c_v2.out)
 
         # Read current revision
-        self.assertEqual(pref.ref.revision, self.c_v2.recipe_revision(self.ref))
+        self.assertEqual(pref.ref.revision, self.recipe_revision(self.ref))
 
     def test_revision_metadata_update_on_install(self):
         """If a clean v2 client installs a RREV/PREV from a server, it get
         the revision from upstream"""
         # Upload with v2
-        pref = self.c_v2.create(self.ref)
-        self.c_v2.upload_all(self.ref)
+        pref = _create(self.c_v2, self.ref)
+        self.c_v2.run(f"upload {self.ref} -r=default -c")
 
         # Remove all from c_v2 local
         self.c_v2.remove_all()
         assert len(self.c_v2.cache.get_recipe_revisions_references(self.ref)) == 0
 
         self.c_v2.run("install --requires={}".format(self.ref))
-        local_rev = self.c_v2.recipe_revision(self.ref)
-        local_prev = self.c_v2.package_revision(pref)
+        local_rev = self.recipe_revision(self.ref)
+        local_prev = self.package_revision(pref)
         self.assertEqual(local_rev, pref.ref.revision)
         self.assertEqual(local_prev, pref.revision)
 
@@ -135,42 +158,43 @@ class InstallingPackagesWithRevisionsTest(unittest.TestCase):
         Another client v2 upload the same RREV with PREV2
         The first client can upgrade from the remote, only
         in the package, because the recipe is the same and it is not updated"""
-        client = TurboTestClient(servers={"default": self.server}, inputs=["admin", "password"])
-        client2 = TurboTestClient(servers={"default": self.server}, inputs=["admin", "password"])
+        client = TestClient(servers={"default": self.server}, inputs=["admin", "password"])
+        client2 = TestClient(servers={"default": self.server}, inputs=["admin", "password"])
 
-        conanfile = GenConanfile().with_package_file("file", env_var="MY_VAR")
+        conanfile = GenConanfile("pkg", "0.1").with_package_file("file", env_var="MY_VAR")
+        client.save({"conanfile.py": conanfile})
         with environment_update({"MY_VAR": "1"}):
-            pref = client.create(self.ref, conanfile=conanfile)
+            client.run("create .")
+            pref = client.created_package_reference("pkg/0.1")
 
         time.sleep(1)
 
         with patch.object(RevisionList, '_now', return_value=time.time()):
-            client.upload_all(self.ref)
+            client.run(f"upload * -r=default -c")
 
+        client2.save({"conanfile.py": conanfile})
         with environment_update({"MY_VAR": "2"}):
-            pref2 = client2.create(self.ref, conanfile=conanfile)
+            client2.run("create .")
+            pref2 = client2.created_package_reference("pkg/0.1")
 
         with patch.object(RevisionList, '_now', return_value=time.time() + 20.0):
-            client2.upload_all(self.ref)
+            client2.run(f"upload * -r=default -c")
 
         prev1_time_remote = self.server.package_revision_time(pref)
         prev2_time_remote = self.server.package_revision_time(pref2)
         self.assertNotEqual(prev1_time_remote, prev2_time_remote)  # Two package revisions
 
-        client.run("install --requires={} --update".format(self.ref))
-        client.assert_listed_require({str(self.ref): "Cache (Updated date) (default)"})
+        client.run("install --requires=pkg/0.1 --update")
+        client.assert_listed_require({"pkg/0.1": "Cache (Updated date) (default)"})
         self.assertIn("Retrieving package {}".format(pref.package_id), client.out)
 
-        prev = client.package_revision(pref)
-        self.assertIsNotNone(prev)
-
     def test_revision_mismatch_packages_in_local(self):
         """Test adapted for the new cache: we create a revision but we export again a  recipe
         to create a new revision, then we won't have a package for the latest recipe revision
         of the cache.
         TODO: cache2.0 check this case"""
         client = self.c_v2
-        pref = client.create(self.ref)
+        pref = _create(self.c_v2, self.ref)
         ref2 = client.export(self.ref, conanfile=GenConanfile().with_build_msg("REV2"))
         # Now we have two RREVs and a PREV corresponding to the first one
         sot1 = copy.copy(pref.ref)
@@ -198,9 +222,11 @@ class InstallingPackagesWithRevisionsTest(unittest.TestCase):
         self.assertIn("Unable to find '{}#fakerevision' in remotes".format(ref), client.out)
 
         # Now create a new revision with other client and upload it, we will request it
-        new_client = TurboTestClient(servers=self.servers, inputs=["admin", "password"])
-        pref = new_client.create(self.ref, conanfile=GenConanfile().with_build_msg("Rev2"))
-        new_client.upload_all(self.ref)
+        new_client = TestClient(servers=self.servers, inputs=["admin", "password"])
+        new_client.save({"conanfile.py": GenConanfile("lib", "1.0").with_build_msg("Rev2")})
+        new_client.run("create . --user=conan --channel=testing")
+        pref = new_client.created_package_reference(self.ref)
+        new_client.run(f"upload * -r=default -c")
 
         # Repeat the install --update pointing to the new reference
         client.run("install --requires={} --update".format(repr(pref.ref)))
@@ -209,8 +235,8 @@ class InstallingPackagesWithRevisionsTest(unittest.TestCase):
     def test_revision_mismatch_packages_remote(self):
         """If we have a recipe that doesn't match a remote recipe:
          It is not resolved in the remote."""
-        self.c_v2.create(self.ref)
-        self.c_v2.upload_all(self.ref)
+        _create(self.c_v2, self.ref)
+        self.c_v2.run(f"upload {self.ref} -r=default -c")
 
         client = self.c_v2
         client.remove_all()
@@ -226,10 +252,10 @@ class InstallingPackagesWithRevisionsTest(unittest.TestCase):
         refs = []
         for _ in range(1, 4):  # create different revisions
             conanfile.with_build_msg("any change to get another rrev")
-            pref = self.c_v2.create(self.ref, conanfile=conanfile)
-            self.c_v2.upload_all(pref.ref)
+            pref = _create(self.c_v2, self.ref, conanfile=conanfile)
+            self.c_v2.run(f"upload {pref.ref} -r=default -c")
             refs.append(pref.ref)
-            assert refs.count(pref.ref) == 1 # make sure that all revisions are different
+            assert refs.count(pref.ref) == 1  # make sure that all revisions are different
 
         client = self.c_v2  # revisions enabled
         client.remove_all()
@@ -244,7 +270,7 @@ class RemoveWithRevisionsTest(unittest.TestCase):
 
     def setUp(self):
         self.server = TestServer()
-        self.c_v2 = TurboTestClient(servers={"default": self.server}, inputs=["admin", "password"])
+        self.c_v2 = TestClient(servers={"default": self.server}, inputs=["admin", "password"])
         self.ref = RecipeReference.loads("lib/1.0@conan/testing")
 
     def test_remove_local_recipe(self):
@@ -275,14 +301,14 @@ class RemoveWithRevisionsTest(unittest.TestCase):
         client = self.c_v2
 
         # If I remove the ref without RREV, the packages are also removed
-        pref1 = client.create(self.ref)
+        pref1 = _create(self.c_v2, self.ref)
         tmp = copy.copy(pref1.ref)
         tmp.revision = None
         client.run("remove {} -c".format(repr(tmp)))
         self.assertFalse(client.package_exists(pref1))
 
         # If I remove the ref with fake RREV, the packages are not removed
-        pref1 = client.create(self.ref)
+        pref1 = _create(self.c_v2, self.ref)
         fakeref = copy.copy(pref1.ref)
         fakeref.revision = "fakerev"
         str_ref = repr(fakeref)
@@ -291,12 +317,12 @@ class RemoveWithRevisionsTest(unittest.TestCase):
         self.assertIn("Recipe revision '{}' not found".format(str_ref), client.out)
 
         # If I remove the ref with valid RREV, the packages are removed
-        pref1 = client.create(self.ref)
+        pref1 = _create(self.c_v2, self.ref)
         client.run("remove {} -c".format(repr(pref1.ref)))
         self.assertFalse(client.package_exists(pref1))
 
         # If I remove the ref without RREV but specifying PREV it raises
-        pref1 = client.create(self.ref)
+        pref1 = _create(self.c_v2, self.ref)
         tmp = copy.copy(pref1.ref)
         tmp.revision = None
         command = "remove {}:{}#{} -c".format(repr(tmp), pref1.package_id, pref1.revision)
@@ -304,14 +330,14 @@ class RemoveWithRevisionsTest(unittest.TestCase):
         self.assertFalse(client.package_exists(pref1))
 
         # A wrong PREV doesn't remove the PREV
-        pref1 = client.create(self.ref)
+        pref1 = _create(self.c_v2, self.ref)
         command = "remove {}:{}#fakeprev -c".format(repr(pref1.ref), pref1.package_id)
         client.run(command, assert_error=True)
         self.assertTrue(client.package_exists(pref1))
         self.assertIn("ERROR: Package revision", client.out)
 
         # Everything correct, removes the unique local package revision
-        pref1 = client.create(self.ref)
+        pref1 = _create(self.c_v2, self.ref)
         command = "remove {}:{}#{} -c".format(repr(pref1.ref), pref1.package_id, pref1.revision)
         client.run(command)
         self.assertFalse(client.package_exists(pref1))
@@ -319,12 +345,11 @@ class RemoveWithRevisionsTest(unittest.TestCase):
     def test_remove_remote_recipe(self):
         """When a client removes a reference, it removes ALL revisions, no matter
         if the client is v1 or v2"""
-        pref1 = self.c_v2.create(self.ref)
-        self.c_v2.upload_all(pref1.ref)
+        pref1 = _create(self.c_v2, self.ref)
+        self.c_v2.run(f"upload {pref1.ref} -r=default -c")
 
-        pref2 = self.c_v2.create(self.ref,
-                                 conanfile=GenConanfile().with_build_msg("RREV 2!"))
-        self.c_v2.upload_all(pref2.ref)
+        pref2 = _create(self.c_v2, self.ref, conanfile=GenConanfile().with_build_msg("RREV 2!"))
+        self.c_v2.run(f"upload {pref2.ref} -r=default -c")
 
         self.assertNotEqual(pref1, pref2)
 
@@ -341,12 +366,11 @@ class RemoveWithRevisionsTest(unittest.TestCase):
     def test_remove_remote_recipe_revision(self):
         """If a client removes a recipe with revision:
              - If the client is v2 will remove only that revision"""
-        pref1 = self.c_v2.create(self.ref)
-        self.c_v2.upload_all(pref1.ref)
+        pref1 = _create(self.c_v2, self.ref)
+        self.c_v2.run(f"upload {pref1.ref} -r=default -c")
 
-        pref2 = self.c_v2.create(self.ref,
-                                 conanfile=GenConanfile().with_build_msg("RREV 2!"))
-        self.c_v2.upload_all(pref2.ref)
+        pref2 = _create(self.c_v2, self.ref, conanfile=GenConanfile().with_build_msg("RREV 2!"))
+        self.c_v2.run(f"upload {pref2.ref} -r=default -c")
 
         self.assertNotEqual(pref1, pref2)
 
@@ -361,12 +385,11 @@ class RemoveWithRevisionsTest(unittest.TestCase):
     def test_remove_remote_package(self):
         """When a client removes a package, without RREV, it removes the package from ALL
         RREVs"""
-        pref1 = self.c_v2.create(self.ref)
-        self.c_v2.upload_all(pref1.ref)
+        pref1 = _create(self.c_v2, self.ref)
+        self.c_v2.run(f"upload {pref1.ref} -r=default -c")
 
-        pref2 = self.c_v2.create(self.ref,
-                                 conanfile=GenConanfile().with_build_msg("RREV 2!"))
-        self.c_v2.upload_all(pref2.ref)
+        pref2 = _create(self.c_v2, self.ref, conanfile=GenConanfile().with_build_msg("RREV 2!"))
+        self.c_v2.run(f"upload {pref2.ref} -r=default -c")
 
         self.assertEqual(pref1.package_id, pref2.package_id)
         # Both revisions exist in 2.0 cache
@@ -391,19 +414,19 @@ class RemoveWithRevisionsTest(unittest.TestCase):
                 - If v2 it removes only that PREV
         """
         # First RREV
-        pref1 = self.c_v2.create(self.ref)
-        self.c_v2.upload_all(pref1.ref)
+        pref1 = _create(self.c_v2, self.ref)
+        self.c_v2.run(f"upload {pref1.ref} -r=default -c")
 
         # Second RREV with two PREVS (exactly same conanfile, different package files)
         rev2_conanfile = GenConanfile().with_build_msg("RREV 2!")\
                                        .with_package_file("file", env_var="MY_VAR")
         with environment_update({"MY_VAR": "1"}):
-            pref2 = self.c_v2.create(self.ref, conanfile=rev2_conanfile)
-            self.c_v2.upload_all(pref2.ref)
+            pref2 = _create(self.c_v2, self.ref, conanfile=rev2_conanfile)
+            self.c_v2.run(f"upload {pref2.ref} -r=default -c")
 
         with environment_update({"MY_VAR": "2"}):
-            pref2b = self.c_v2.create(self.ref, conanfile=rev2_conanfile)
-            self.c_v2.upload_all(pref2b.ref)
+            pref2b = _create(self.c_v2, self.ref, conanfile=rev2_conanfile)
+            self.c_v2.run(f"upload {pref2b.ref} -r=default -c")
 
         # Check created revisions
         self.assertEqual(pref1.package_id, pref2.package_id)
@@ -437,7 +460,7 @@ class UploadPackagesWithRevisions(unittest.TestCase):
 
     def setUp(self):
         self.server = TestServer()
-        self.c_v2 = TurboTestClient(servers={"default": self.server}, inputs=["admin", "password"])
+        self.c_v2 = TestClient(servers={"default": self.server}, inputs=["admin", "password"])
         self.ref = RecipeReference.loads("lib/1.0@conan/testing")
 
     def test_upload_a_recipe(self):
@@ -445,8 +468,8 @@ class UploadPackagesWithRevisions(unittest.TestCase):
         Using v2 client it will upload RREV revision to the server. The rev time is NOT updated.
         """
         client = self.c_v2
-        pref = client.create(self.ref)
-        client.upload_all(self.ref)
+        pref = _create(self.c_v2, self.ref)
+        client.run(f"upload {self.ref} -r=default -c")
         revs = [r.revision for r in self.server.server_store.get_recipe_revisions_references(self.ref)]
 
         self.assertEqual(revs, [pref.ref.revision])
@@ -457,15 +480,16 @@ class UploadPackagesWithRevisions(unittest.TestCase):
         Using v2 client it will warn an upload a new revision.
         """
         client = self.c_v2
-        pref = client.create(self.ref, conanfile=GenConanfile().with_setting("os"),
-                             args=" -s os=Windows")
-        client.upload_all(self.ref)
-        pref2 = client.create(self.ref,
-                              conanfile=GenConanfile().with_setting("os").with_build_msg("rev2"),
-                              args=" -s os=Linux")
+        pref = _create(self.c_v2, self.ref, conanfile=GenConanfile().with_setting("os"),
+                       args=" -s os=Windows")
+        client.run(f"upload {self.ref} -r=default -c")
+
+        pref2 = _create(self.c_v2, self.ref,
+                        conanfile=GenConanfile().with_setting("os").with_build_msg("rev2"),
+                        args=" -s os=Linux")
 
         self.assertEqual(self.server.server_store.get_last_revision(self.ref)[0], pref.ref.revision)
-        client.upload_all(self.ref)
+        client.run(f"upload {self.ref} -r=default -c")
         self.assertEqual(self.server.server_store.get_last_revision(self.ref)[0], pref2.ref.revision)
 
     def test_upload_no_overwrite_packages(self):
@@ -476,35 +500,34 @@ class UploadPackagesWithRevisions(unittest.TestCase):
         client = self.c_v2
         conanfile = GenConanfile().with_package_file("file", env_var="MY_VAR")
         with environment_update({"MY_VAR": "1"}):
-            pref = client.create(self.ref, conanfile=conanfile)
-        client.upload_all(self.ref)
+            pref = _create(self.c_v2, self.ref, conanfile=conanfile)
+        client.run(f"upload {self.ref} -r=default -c")
 
         with environment_update({"MY_VAR": "2"}):
-            pref2 = client.create(self.ref, conanfile=conanfile)
+            pref2 = _create(self.c_v2, self.ref, conanfile=conanfile)
 
         self.assertNotEqual(pref.revision, pref2.revision)
 
         self.assertEqual(self.server.server_store.get_last_package_revision(pref2).revision,
                          pref.revision)
-        client.upload_all(self.ref)
+        client.run(f"upload {self.ref} -r=default -c")
         self.assertEqual(self.server.server_store.get_last_package_revision(pref2).revision,
                          pref2.revision)
 
 
-class CapabilitiesRevisionsTest(unittest.TestCase):
-    def test_server_with_only_v2_capability(self):
-        server = TestServer(server_capabilities=[])
-        c_v2 = TurboTestClient(servers={"default": server}, inputs=["admin", "password"])
-        ref = RecipeReference.loads("lib/1.0@conan/testing")
-        c_v2.create(ref)
-        c_v2.upload_all(ref, remote="default")
+def test_server_with_only_v2_capability():
+    server = TestServer(server_capabilities=[])
+    c_v2 = TestClient(servers={"default": server}, inputs=["admin", "password"])
+    c_v2.save({"conanfile.py": GenConanfile("pkg", "0.1")})
+    c_v2.run("create .")
+    c_v2.run(f"upload * -r=default -c")
 
 
 class ServerRevisionsIndexes(unittest.TestCase):
 
     def setUp(self):
         self.server = TestServer()
-        self.c_v2 = TurboTestClient(servers={"default": self.server}, inputs=["admin", "password"])
+        self.c_v2 = TestClient(servers={"default": self.server}, inputs=["admin", "password"])
         self.ref = RecipeReference.loads("lib/1.0@conan/testing")
 
     def test_rotation_deleting_recipe_revisions(self):
@@ -515,15 +538,15 @@ class ServerRevisionsIndexes(unittest.TestCase):
         the first is now the latest
         """
         ref1 = self.c_v2.export(self.ref, conanfile=GenConanfile())
-        self.c_v2.upload_all(ref1)
+        self.c_v2.run(f"upload {ref1} -r=default -c")
         self.assertEqual(self.server.server_store.get_last_revision(self.ref).revision,
                          ref1.revision)
         ref2 = self.c_v2.export(self.ref, conanfile=GenConanfile().with_build_msg("I'm rev2"))
-        self.c_v2.upload_all(ref2)
+        self.c_v2.run(f"upload {ref2} -r=default -c")
         self.assertEqual(self.server.server_store.get_last_revision(self.ref).revision,
                          ref2.revision)
         ref3 = self.c_v2.export(self.ref, conanfile=GenConanfile().with_build_msg("I'm rev3"))
-        self.c_v2.upload_all(ref3)
+        self.c_v2.run(f"upload {ref2} -r=default -c")
         self.assertEqual(self.server.server_store.get_last_revision(self.ref).revision,
                          ref3.revision)
 
@@ -548,24 +571,24 @@ class ServerRevisionsIndexes(unittest.TestCase):
         """
         conanfile = GenConanfile().with_package_file("file", env_var="MY_VAR")
         with environment_update({"MY_VAR": "1"}):
-            pref1 = self.c_v2.create(self.ref, conanfile=conanfile)
-        self.c_v2.upload_all(self.ref)
+            pref1 = _create(self.c_v2, self.ref, conanfile=conanfile)
+        self.c_v2.run("upload * -r=default -c")
         self.assertEqual(self.server.server_store.get_last_package_revision(pref1).revision,
                          pref1.revision)
         with environment_update({"MY_VAR": "2"}):
-            pref2 = self.c_v2.create(self.ref, conanfile=conanfile)
-        self.c_v2.upload_all(self.ref)
+            pref2 = _create(self.c_v2, self.ref, conanfile=conanfile)
+        self.c_v2.run("upload * -r=default -c")
         self.assertEqual(self.server.server_store.get_last_package_revision(pref1).revision,
                          pref2.revision)
         with environment_update({"MY_VAR": "3"}):
-            pref3 = self.c_v2.create(self.ref, conanfile=conanfile)
-        server_pref3 = self.c_v2.upload_all(self.ref)
+            pref3 = _create(self.c_v2, self.ref, conanfile=conanfile)
+        self.c_v2.run("upload * -r=default -c")
         self.assertEqual(self.server.server_store.get_last_package_revision(pref1).revision,
                          pref3.revision)
 
         self.assertEqual(pref1.ref.revision, pref2.ref.revision)
         self.assertEqual(pref2.ref.revision, pref3.ref.revision)
-        self.assertEqual(pref3.ref.revision, server_pref3.revision)
+        self.assertEqual(pref3.ref.revision, pref3.ref.revision)
 
         pref = copy.copy(pref1)
         pref.revision = None
@@ -576,7 +599,7 @@ class ServerRevisionsIndexes(unittest.TestCase):
                          pref3.revision)
 
         # Delete the latest from the server
-        self.c_v2.run("remove {}:{}#{} -r default -c".format(repr(pref3.ref),pref3.package_id,
+        self.c_v2.run("remove {}:{}#{} -r default -c".format(repr(pref3.ref), pref3.package_id,
                                                              pref3.revision))
         revs = [r.revision
                 for r in self.server.server_store.get_package_revisions_references(pref)]
@@ -590,11 +613,11 @@ class ServerRevisionsIndexes(unittest.TestCase):
         If then a client uploads a RREV it is the latest
         """
         ref1 = self.c_v2.export(self.ref, conanfile=GenConanfile())
-        self.c_v2.upload_all(ref1)
+        self.c_v2.run("upload * -r=default -c")
         ref2 = self.c_v2.export(self.ref, conanfile=GenConanfile().with_build_msg("I'm rev2"))
-        self.c_v2.upload_all(ref2)
+        self.c_v2.run("upload * -r=default -c")
         ref3 = self.c_v2.export(self.ref, conanfile=GenConanfile().with_build_msg("I'm rev3"))
-        self.c_v2.upload_all(ref3)
+        self.c_v2.run("upload * -r=default -c")
 
         self.c_v2.run("remove {} -r default -c".format(repr(ref1)))
         self.c_v2.run("remove {} -r default -c".format(repr(ref2)))
@@ -604,7 +627,7 @@ class ServerRevisionsIndexes(unittest.TestCase):
                           self.server.server_store.get_recipe_revisions_references, self.ref)
 
         ref4 = self.c_v2.export(self.ref, conanfile=GenConanfile().with_build_msg("I'm rev4"))
-        self.c_v2.upload_all(ref4)
+        self.c_v2.run("upload * -r=default -c")
 
         revs = [r.revision for r in self.server.server_store.get_recipe_revisions_references(self.ref)]
         self.assertEqual(revs, [ref4.revision])
@@ -616,14 +639,14 @@ class ServerRevisionsIndexes(unittest.TestCase):
         """
         conanfile = GenConanfile().with_package_file("file", env_var="MY_VAR")
         with environment_update({"MY_VAR": "1"}):
-            pref1 = self.c_v2.create(self.ref, conanfile=conanfile)
-        self.c_v2.upload_all(self.ref)
+            pref1 = _create(self.c_v2, self.ref, conanfile=conanfile)
+        self.c_v2.run(f"upload {self.ref} -r=default -c")
         with environment_update({"MY_VAR": "2"}):
-            pref2 = self.c_v2.create(self.ref, conanfile=conanfile)
-        self.c_v2.upload_all(self.ref)
+            pref2 = _create(self.c_v2, self.ref, conanfile=conanfile)
+        self.c_v2.run(f"upload {self.ref} -r=default -c")
         with environment_update({"MY_VAR": "3"}):
-            pref3 = self.c_v2.create(self.ref, conanfile=conanfile)
-        self.c_v2.upload_all(self.ref)
+            pref3 = _create(self.c_v2, self.ref, conanfile=conanfile)
+        self.c_v2.run(f"upload {self.ref} -r=default -c")
 
         # Delete the package revisions (all of them have the same ref#rev and id)
         command = "remove {}:{}#{{}} -r default -c".format(pref3.ref.repr_notime(), pref3.package_id)
@@ -632,7 +655,7 @@ class ServerRevisionsIndexes(unittest.TestCase):
         self.c_v2.run(command.format(pref1.revision))
 
         with environment_update({"MY_VAR": "4"}):
-            pref4 = self.c_v2.create(self.ref, conanfile=conanfile)
+            pref4 = _create(self.c_v2, self.ref, conanfile=conanfile)
         self.c_v2.run("upload {} -r default -c".format(pref4.repr_notime()))
 
         pref = copy.copy(pref1)
diff --git a/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_build_modules.py b/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_build_modules.py
index 8b7774f24..27981e025 100644
--- a/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_build_modules.py
+++ b/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_build_modules.py
@@ -204,3 +204,174 @@ def test_build_modules_components_is_not_possible():
     client.save({"CMakeLists.txt": cmakelists.replace("crypto", "#crypto")})
     assert "ROOT MESSAGE:hello!" not in client.out
 
+
+@pytest.mark.tool("cmake")
+@pytest.mark.parametrize("editable", [True, False])
+def test_build_modules_custom_script_editable(editable):
+    c = TestClient()
+    conanfile = textwrap.dedent(r"""
+        import os, glob
+        from conan import ConanFile
+        from conan.tools.cmake import cmake_layout
+        from conan.tools.files import copy, save
+
+        class Conan(ConanFile):
+            name = "myfunctions"
+            version = "1.0"
+            exports_sources = ["src/*.cmake"]
+            settings = "build_type", "arch"
+
+            def build(self):
+                cmake = 'set(MY_CMAKE_PATH ${CMAKE_CURRENT_LIST_DIR})\n'\
+                        'macro(otherfunc)\n'\
+                        'file(READ "${MY_CMAKE_PATH}/my.txt" c)\n'\
+                        'message("Hello ${c}!!!!")\nendmacro()'
+                save(self, "otherfuncs.cmake", cmake)
+                save(self, "my.txt", "contents of text file!!!!")
+
+            def layout(self):
+                cmake_layout(self, src_folder="src")
+                src = glob.glob(os.path.join(self.recipe_folder, self.folders.source, "*.cmake"))
+                build = glob.glob(os.path.join(self.recipe_folder, self.folders.build, "*.cmake"))
+                self.cpp.source.set_property("cmake_build_modules", src)
+                self.cpp.build.set_property("cmake_build_modules", build)
+
+            def package(self):
+                copy(self, "*.cmake", self.source_folder, os.path.join(self.package_folder, "mods"),
+                     keep_path=False)
+                copy(self, "*.cmake", self.build_folder, os.path.join(self.package_folder, "mods"),
+                     keep_path=False)
+                copy(self, "*.txt", self.build_folder, os.path.join(self.package_folder, "mods"),
+                     keep_path=False)
+
+            def package_info(self):
+                self.cpp_info.set_property("cmake_build_modules", glob.glob("mods/*.cmake"))
+        """)
+
+    myfunction = textwrap.dedent("""
+        function(myfunction)
+            message("Hello myfunction!!!!")
+        endfunction()
+        """)
+
+    consumer = textwrap.dedent("""
+        from conan import ConanFile
+        from conan.tools.cmake import CMake
+
+        class Conan(ConanFile):
+            settings = "os", "compiler", "build_type", "arch"
+            generators = "CMakeToolchain", "CMakeDeps"
+            requires = "myfunctions/1.0"
+
+            def build(self):
+                cmake = CMake(self)
+                cmake.configure()
+        """)
+    cmakelists = textwrap.dedent("""
+        cmake_minimum_required(VERSION 3.15)
+        project(test)
+        find_package(myfunctions CONFIG REQUIRED)
+        myfunction()
+        otherfunc()
+        """)
+    c.save({"functions/conanfile.py": conanfile,
+            "functions/src/myfunction.cmake": myfunction,
+            "app/conanfile.py": consumer,
+            "app/CMakeLists.txt": cmakelists})
+
+    if editable:
+        c.run("editable add functions")
+        c.run('build functions -c tools.cmake.cmake_layout:build_folder_vars="[\'settings.arch\']"')
+    else:
+        c.run("create functions")
+    c.run('build app -c tools.cmake.cmake_layout:build_folder_vars="[\'settings.arch\']"')
+    assert "Hello myfunction!!!!" in c.out
+    assert "Hello contents of text file!!!!" in c.out
+
+
+@pytest.mark.tool("cmake")
+@pytest.mark.parametrize("editable", [True, False])
+def test_build_modules_custom_script_editable_package(editable):
+    # same as above, but with files from the package folder
+    c = TestClient()
+    conanfile = textwrap.dedent(r"""
+        import os, glob
+        from conan import ConanFile
+        from conan.tools.cmake import cmake_layout
+        from conan.tools.files import copy, save
+
+        class Conan(ConanFile):
+            name = "myfunctions"
+            version = "1.0"
+            exports_sources = ["src/*.cmake", "src/vis/*.vis"]
+            settings = "build_type", "arch"
+
+            def build(self):
+                config = str(self.settings.build_type).upper()
+                cmake = f'set(MY_CMAKE_PATH ${{{self.name}_RES_DIRS_{config}}})\n'\
+                        'macro(otherfunc)\n'\
+                        'file(READ "${MY_CMAKE_PATH}/my.vis" c)\n'\
+                        'message("Hello ${c}!!!!")\nendmacro()'
+                save(self, "otherfuncs.cmake", cmake)
+
+            def layout(self):
+                cmake_layout(self, src_folder="src")
+                src = glob.glob(os.path.join(self.recipe_folder, self.folders.source, "*.cmake"))
+                build = glob.glob(os.path.join(self.recipe_folder, self.folders.build, "*.cmake"))
+                self.cpp.source.set_property("cmake_build_modules", src)
+                self.cpp.build.set_property("cmake_build_modules", build)
+                self.cpp.source.resdirs = ["vis"]
+
+            def package(self):
+                copy(self, "*.cmake", self.source_folder, os.path.join(self.package_folder, "mods"),
+                     keep_path=False)
+                copy(self, "*.cmake", self.build_folder, os.path.join(self.package_folder, "mods"),
+                     keep_path=False)
+                copy(self, "*.vis", self.source_folder, os.path.join(self.package_folder, "mods"),
+                     keep_path=False)
+
+            def package_info(self):
+                self.cpp_info.resdirs = ["mods"]
+                self.cpp_info.set_property("cmake_build_modules", glob.glob("mods/*.cmake"))
+        """)
+
+    myfunction = textwrap.dedent("""
+        function(myfunction)
+            message("Hello myfunction!!!!")
+        endfunction()
+        """)
+
+    consumer = textwrap.dedent("""
+        from conan import ConanFile
+        from conan.tools.cmake import CMake
+
+        class Conan(ConanFile):
+            settings = "os", "compiler", "build_type", "arch"
+            generators = "CMakeToolchain", "CMakeDeps"
+            requires = "myfunctions/1.0"
+
+            def build(self):
+                cmake = CMake(self)
+                cmake.configure()
+        """)
+    cmakelists = textwrap.dedent("""
+        cmake_minimum_required(VERSION 3.15)
+        project(test)
+        find_package(myfunctions CONFIG REQUIRED)
+        myfunction()
+        otherfunc()
+        """)
+    c.save({"functions/conanfile.py": conanfile,
+            "functions/src/myfunction.cmake": myfunction,
+            "functions/src/vis/my.vis": "contents of text file!!!!",
+            "app/conanfile.py": consumer,
+            "app/CMakeLists.txt": cmakelists})
+
+    if editable:
+        c.run("editable add functions")
+        c.run('build functions -c tools.cmake.cmake_layout:build_folder_vars="[\'settings.arch\']"')
+    else:
+        c.run("create functions -vv")
+    c.run('build app -c tools.cmake.cmake_layout:build_folder_vars="[\'settings.arch\']"')
+    assert "Hello myfunction!!!!" in c.out
+    assert "Hello contents of text file!!!!" in c.out
diff --git a/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_new.py b/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_new.py
index b4c7d0926..044865fe3 100644
--- a/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_new.py
+++ b/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_new.py
@@ -1164,3 +1164,85 @@ class TestCMakeTry:
         preset = "conan-default" if platform.system() == "Windows" else "conan-release"
         c.run_command(f"cmake --preset {preset} ")
         assert "Performing Test IT_COMPILES - Success" in c.out
+
+
+class TestCMakeComponents:
+
+    @pytest.mark.tool("cmake")
+    @pytest.mark.parametrize("components, found", [("comp1", True), ("compX", False)])
+    def test_components(self, components, found):
+        c = TestClient()
+        dep = textwrap.dedent("""
+            from conan import ConanFile
+            class Pkg(ConanFile):
+                name = "dep"
+                version = "0.1"
+                def package_info(self):
+                    self.cpp_info.set_property("cmake_components", ["comp1", "comp2"])
+            """)
+        c.save({"conanfile.py": dep})
+        c.run("create .")
+
+        consumer = textwrap.dedent("""
+            from conan import ConanFile
+            from conan.tools.cmake import CMake
+            class PkgConan(ConanFile):
+                settings = "os", "arch", "compiler", "build_type"
+                requires = "dep/0.1"
+                generators = "CMakeToolchain", "CMakeDeps"
+                def build(self):
+                    deps = CMake(self)
+                    deps.configure()
+            """)
+
+        cmakelist = textwrap.dedent(f"""\
+            cmake_minimum_required(VERSION 3.15)
+            project(Hello LANGUAGES NONE)
+
+            find_package(dep CONFIG REQUIRED COMPONENTS {components})
+            """)
+
+        c.save({"conanfile.py": consumer,
+                "CMakeLists.txt": cmakelist}, clean_first=True)
+        c.run(f"build . -c tools.cmake.cmakedeps:new={new_value}", assert_error=not found)
+        if not found:
+            assert f"Conan: Error: 'dep' required COMPONENT '{components}' not found" in c.out
+
+    def test_components_default_definition(self):
+        c = TestClient()
+        dep = textwrap.dedent("""
+            from conan import ConanFile
+            class Pkg(ConanFile):
+                name = "dep"
+                version = "0.1"
+                def package_info(self):
+                    # private component that should be skipped
+                    self.cpp_info.components["_private"].includedirs = ["include"]
+                    self.cpp_info.components["c1"].set_property("cmake_target_name", "MyC1")
+                    self.cpp_info.components["c2"].set_property("cmake_target_name", "Dep::MyC2")
+                    self.cpp_info.components["c3"].includedirs = ["include"]
+            """)
+        c.save({"conanfile.py": dep})
+        c.run("create .")
+        c.run(f"install --requires=dep/0.1 -g CMakeDeps -c tools.cmake.cmakedeps:new={new_value}")
+        cmake = c.load("dep-config.cmake")
+        assert 'set(dep_PACKAGE_PROVIDED_COMPONENTS MyC1 MyC2 c3)' in cmake
+
+    def test_components_individual_names(self):
+        c = TestClient()
+        dep = textwrap.dedent("""
+            from conan import ConanFile
+            class Pkg(ConanFile):
+                name = "dep"
+                version = "0.1"
+                def package_info(self):
+                    self.cpp_info.components["c1"].set_property("cmake_target_name", "MyC1")
+                    self.cpp_info.components["c1"].set_property("cmake_components", ["MyCompC1"])
+                    self.cpp_info.components["c2"].set_property("cmake_target_name", "Dep::MyC2")
+                    self.cpp_info.components["c3"].includedirs = ["include"]
+            """)
+        c.save({"conanfile.py": dep})
+        c.run("create .")
+        c.run(f"install --requires=dep/0.1 -g CMakeDeps -c tools.cmake.cmakedeps:new={new_value}")
+        cmake = c.load("dep-config.cmake")
+        assert 'set(dep_PACKAGE_PROVIDED_COMPONENTS MyCompC1 MyC2 c3)' in cmake
diff --git a/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_new_paths.py b/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_new_paths.py
index f3c04e165..862444722 100644
--- a/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_new_paths.py
+++ b/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_new_paths.py
@@ -47,10 +47,12 @@ def test_cmake_generated(client):
 
 
 @pytest.mark.tool("cmake")
-def test_cmake_in_package(client):
+@pytest.mark.parametrize("lowercase", [False, True])
+def test_cmake_in_package(client, lowercase):
     c = client
     # same, but in-package
-    dep = textwrap.dedent("""
+    f = "dep-config" if lowercase else "depConfig"
+    dep = textwrap.dedent(f"""
         import os
         from conan import ConanFile
         from conan.tools.files import save
@@ -60,7 +62,7 @@ def test_cmake_in_package(client):
 
             def package(self):
                 content = 'message(STATUS "Hello from dep dep-Config.cmake!!!!!")'
-                save(self, os.path.join(self.package_folder, "cmake", "dep-config.cmake"), content)
+                save(self, os.path.join(self.package_folder, "cmake", "{f}.cmake"), content)
             def package_info(self):
                 self.cpp_info.set_property("cmake_find_mode", "none")
                 self.cpp_info.builddirs = ["cmake"]
@@ -95,3 +97,54 @@ class TestRuntimeDirs:
         runtime_lib_dirs = re.search(pattern_lib_dirs, contents).group(1)
         assert "<CONFIG:Release>" in runtime_lib_dirs
         assert "<CONFIG:Debug>" in runtime_lib_dirs
+
+
+@pytest.mark.tool("cmake")
+@pytest.mark.parametrize("requires, tool_requires", [(True, False), (False, True), (True, True)])
+def test_cmaketoolchain_path_find_program(requires, tool_requires):
+    """Test that executables in bindirs of tool_requires can be found with
+    find_program() in consumer CMakeLists.
+    """
+    c = TestClient()
+
+    conanfile = textwrap.dedent("""
+        import os
+        from conan.tools.files import copy
+        from conan import ConanFile
+        class TestConan(ConanFile):
+            name = "tool"
+            version = "1.0"
+            exports_sources = "*"
+            def package(self):
+                copy(self, "*", self.source_folder, os.path.join(self.package_folder, "bin"))
+    """)
+    c.save({"conanfile.py": conanfile, "hello": "", "hello.exe": ""})
+    c.run("create .")
+
+    requires = 'requires = "tool/1.0"' if requires else ""
+    tool_requires = 'tool_requires = "tool/1.0"' if tool_requires else ""
+    conanfile = textwrap.dedent(f"""
+        from conan import ConanFile
+        from conan.tools.cmake import CMake
+        class PkgConan(ConanFile):
+            {requires}
+            {tool_requires}
+            settings = "os", "arch", "compiler", "build_type"
+            generators = "CMakeToolchain", "CMakeDeps"
+            def build(self):
+                cmake = CMake(self)
+                cmake.configure()
+    """)
+    consumer = textwrap.dedent("""
+        cmake_minimum_required(VERSION 3.15)
+        project(MyHello)
+        find_program(HELLOPROG hello)
+        if(HELLOPROG)
+            message(STATUS "Found hello prog: ${HELLOPROG}")
+        endif()
+    """)
+    c.save({"conanfile.py": conanfile, "CMakeLists.txt": consumer}, clean_first=True)
+    c.run(f"build . -c tools.cmake.cmakedeps:new={new_value}")
+    assert "Found hello prog" in c.out
+    if requires and tool_requires:
+        assert "There is already a 'tool/1.0' package contributing to CMAKE_PROGRAM_PATH" in c.out
diff --git a/test/functional/toolchains/cmake/test_cmaketoolchain_paths.py b/test/functional/toolchains/cmake/test_cmaketoolchain_paths.py
index 4e2752c05..799a4e2c6 100644
--- a/test/functional/toolchains/cmake/test_cmaketoolchain_paths.py
+++ b/test/functional/toolchains/cmake/test_cmaketoolchain_paths.py
@@ -3,8 +3,7 @@ import textwrap
 
 import pytest
 
-from conans.model.recipe_ref import RecipeReference
-from conan.test.utils.tools import TestClient, TurboTestClient
+from conan.test.utils.tools import TestClient
 
 ios10_armv8_settings = "-s os=iOS -s os.sdk=iphoneos -s os.version=10.0 -s arch=armv8"
 
diff --git a/test/functional/toolchains/gnu/autotools/test_basic.py b/test/functional/toolchains/gnu/autotools/test_basic.py
index 26657ec34..e9a493304 100644
--- a/test/functional/toolchains/gnu/autotools/test_basic.py
+++ b/test/functional/toolchains/gnu/autotools/test_basic.py
@@ -7,12 +7,11 @@ import re
 import pytest
 
 from conan.tools.env.environment import environment_wrap_command
-from conans.model.recipe_ref import RecipeReference
 from conan.test.assets.autotools import gen_makefile_am, gen_configure_ac, gen_makefile
 from conan.test.assets.genconanfile import GenConanfile
 from conan.test.assets.sources import gen_function_cpp
 from test.functional.utils import check_exe_run, check_vs_runtime
-from conan.test.utils.tools import TestClient, TurboTestClient
+from conan.test.utils.tools import TestClient
 
 
 @pytest.mark.skipif(platform.system() not in ["Linux", "Darwin"], reason="Requires Autotools")
diff --git a/test/integration/command/info/info_test.py b/test/integration/command/info/info_test.py
index e2bde2a0a..8f254820a 100644
--- a/test/integration/command/info/info_test.py
+++ b/test/integration/command/info/info_test.py
@@ -3,8 +3,7 @@ import os
 import textwrap
 
 from conan.cli.exit_codes import ERROR_GENERAL
-from conans.model.recipe_ref import RecipeReference
-from conan.test.utils.tools import NO_SETTINGS_PACKAGE_ID, TestClient, GenConanfile, TurboTestClient
+from conan.test.utils.tools import NO_SETTINGS_PACKAGE_ID, TestClient, GenConanfile
 
 
 class TestBasicCliOutput:
@@ -254,19 +253,6 @@ class TestEditables:
         assert "source_folder: None" in c.out
 
 
-class TestInfoRevisions:
-
-    def test_info_command_showing_revision(self):
-        """If I run 'conan info ref' I get information about the revision only in a v2 client"""
-        client = TurboTestClient()
-        ref = RecipeReference.loads("lib/1.0@conan/testing")
-
-        client.create(ref)
-        client.run("graph info --requires={}".format(ref))
-        revision = client.recipe_revision(ref)
-        assert f"ref: lib/1.0@conan/testing#{revision}" in client.out
-
-
 class TestInfoTestPackage:
     # https://github.com/conan-io/conan/issues/10714
 
diff --git a/test/integration/command/install/install_cascade_test.py b/test/integration/command/install/install_cascade_test.py
index 7dab07260..04114740b 100644
--- a/test/integration/command/install/install_cascade_test.py
+++ b/test/integration/command/install/install_cascade_test.py
@@ -1,80 +1,46 @@
-import unittest
 from collections import OrderedDict
 
-from conans.model.recipe_ref import RecipeReference
-from conan.test.utils.tools import TestServer, TurboTestClient, GenConanfile
-
-
-class InstallCascadeTest(unittest.TestCase):
-
-    def setUp(self):
-        """
-         A
-        / \
-       B   C
-       |    \
-       D    |
-      / \   |
-      |  \ /
-      E   F
-        """
-        server = TestServer()
-        servers = OrderedDict([("default", server)])
-        self.client = TurboTestClient(servers=servers)
-
-        self.ref_a = RecipeReference.loads("liba/1.0@conan/stable")
-        self.client.create(self.ref_a, conanfile=GenConanfile())
-
-        self.ref_b = RecipeReference.loads("libb/1.0@conan/stable")
-        self.client.create(self.ref_b, conanfile=GenConanfile().with_requirement(self.ref_a))
-
-        self.ref_c = RecipeReference.loads("libc/1.0@conan/stable")
-        self.client.create(self.ref_c, conanfile=GenConanfile().with_requirement(self.ref_a))
-
-        self.ref_d = RecipeReference.loads("libd/1.0@conan/stable")
-        self.client.create(self.ref_d, conanfile=GenConanfile().with_requirement(self.ref_b))
-
-        self.ref_e = RecipeReference.loads("libe/1.0@conan/stable")
-        self.client.create(self.ref_e, conanfile=GenConanfile().with_requirement(self.ref_d))
-
-        self.ref_f = RecipeReference.loads("libf/1.0@conan/stable")
-        conanfile = GenConanfile().with_requirement(self.ref_c).with_requirement(self.ref_d)
-        self.client.create(self.ref_f, conanfile=conanfile)
-
-    def _assert_built(self, refs):
+from conan.test.utils.tools import TestServer, GenConanfile, TestClient
+
+
+def test_cascade():
+    """
+    app -> E -> D -> B -> A
+      \\-> F -> C -------/
+    """
+    server = TestServer()
+    servers = OrderedDict([("default", server)])
+    c = TestClient(servers=servers)
+    c.save({"a/conanfile.py": GenConanfile("liba", "1.0"),
+            "b/conanfile.py": GenConanfile("libb", "1.0").with_requires("liba/1.0"),
+            "c/conanfile.py": GenConanfile("libc", "1.0").with_requires("liba/1.0"),
+            "d/conanfile.py": GenConanfile("libd", "1.0").with_requires("libb/1.0"),
+            "e/conanfile.py": GenConanfile("libe", "1.0").with_requires("libd/1.0"),
+            "f/conanfile.py": GenConanfile("libf", "1.0").with_requires("libc/1.0", "libd/1.0"),
+            "app/conanfile.py": GenConanfile().with_requires("libe/1.0", "libf/1.0")})
+
+    for pkg in ("a", "b", "c", "d", "e", "f"):
+        c.run(f"create {pkg}")
+
+    def _assert_built(refs):
         for ref in refs:
-            self.assertIn("{}: Copying sources to build folder".format(ref), self.client.out)
-        for ref in [self.ref_a, self.ref_b, self.ref_c, self.ref_d, self.ref_e, self.ref_f]:
+            assert "{}: Copying sources to build folder".format(ref) in c.out
+        for ref in ["liba/1.0", "libb/1.0", "libc/1.0", "libd/1.0", "libe/1.0", "libf/1.0"]:
             if ref not in refs:
-                self.assertNotIn("{}: Copying sources to build folder".format(ref),
-                                 self.client.out)
-
-    def test_install_cascade_only_affected(self):
-        project = RecipeReference.loads("project/1.0@conan/stable")
-        project_cf = GenConanfile().with_requirement(self.ref_e).with_requirement(self.ref_f)
+                assert "{}: Copying sources to build folder".format(ref) not in c.out
 
-        # Building A everything is built
-        self.client.create(project, conanfile=project_cf,
-                           args="--build {} --build cascade".format(self.ref_a))
-        self._assert_built([self.ref_a, self.ref_b, self.ref_c, self.ref_d,
-                            self.ref_e, self.ref_f, project])
+    # Building A everything is built
+    c.run("install app --build=liba* --build cascade")
+    _assert_built(["liba/1.0", "libb/1.0", "libc/1.0", "libd/1.0", "libe/1.0", "libf/1.0"])
 
-        # Building D builds E, F and project
-        self.client.create(project, conanfile=project_cf,
-                           args="--build {} --build cascade".format(self.ref_d))
-        self._assert_built([self.ref_d, self.ref_e, self.ref_f, project])
+    c.run("install app --build=libd* --build cascade")
+    _assert_built(["libd/1.0", "libe/1.0", "libf/1.0"])
 
-        # Building E only builds E and project
-        self.client.create(project, conanfile=project_cf,
-                           args="--build {} --build cascade".format(self.ref_e))
-        self._assert_built([self.ref_e, project])
+    c.run("install app --build=libe* --build cascade")
+    _assert_built(["libe/1.0"])
 
-        # Building project only builds project
-        self.client.create(project, conanfile=project_cf,
-                           args="--build {} --build cascade".format(project))
-        self._assert_built([project])
+    c.run("install app  --build cascade")
+    _assert_built([])
 
-        # Building C => builds F and project
-        self.client.create(project, conanfile=project_cf,
-                           args="--build {} --build cascade".format(self.ref_c))
-        self._assert_built([project, self.ref_f, self.ref_c])
+    c.run("install app --build=libc* --build cascade")
+    _assert_built(["libc/1.0", "libf/1.0"])
diff --git a/test/integration/command/new_test.py b/test/integration/command/new_test.py
index 0dbb71dea..55d5103b8 100644
--- a/test/integration/command/new_test.py
+++ b/test/integration/command/new_test.py
@@ -11,7 +11,7 @@ from conans.util.files import save
 
 class TestNewCommand:
     def test_new_cmake_lib(self):
-        client = TestClient()
+        client = TestClient(light=True)
         client.run("new cmake_lib -d name=pkg -d version=1.3")
         conanfile = client.load("conanfile.py")
         assert "CMakeToolchain" in conanfile
@@ -25,7 +25,7 @@ class TestNewCommand:
         assert "pkg/1.3@myuser/testing" in client.out
 
     def test_new_cmake_exe(self):
-        client = TestClient()
+        client = TestClient(light=True)
         client.run("new cmake_exe -d name=pkg -d version=1.3")
         conanfile = client.load("conanfile.py")
         assert "CMakeToolchain" in conanfile
@@ -37,7 +37,7 @@ class TestNewCommand:
         assert "pkg/1.3@myuser/testing" in client.out
 
     def test_new_basic_template(self):
-        tc = TestClient()
+        tc = TestClient(light=True)
         tc.run("new basic")
         assert '# self.requires("zlib/1.2.13")' in tc.load("conanfile.py")
 
@@ -48,7 +48,7 @@ class TestNewCommand:
         assert 'name = "mygame"' in conanfile
 
     def test_new_defaults(self):
-        c = TestClient()
+        c = TestClient(light=True)
         for t in ("cmake_lib", "cmake_exe", "meson_lib", "meson_exe", "msbuild_lib", "msbuild_exe",
                   "bazel_lib", "bazel_exe", "autotools_lib", "autotools_exe"):
             c.run(f"new {t} -f")
@@ -66,7 +66,7 @@ class TestNewCommandUserTemplate:
 
     @pytest.mark.parametrize("folder", ("mytemplate", "sub/mytemplate"))
     def test_user_template(self, folder):
-        client = TestClient()
+        client = TestClient(light=True)
         template1 = textwrap.dedent("""
             class Conan(ConanFile):
                 name = "{{name}}"
@@ -83,7 +83,7 @@ class TestNewCommandUserTemplate:
 
     def test_user_template_abs(self):
         tmp_folder = temp_folder()
-        client = TestClient()
+        client = TestClient(light=True)
         template1 = textwrap.dedent("""
             class Conan(ConanFile):
                 name = "{{name}}"
@@ -94,13 +94,13 @@ class TestNewCommandUserTemplate:
         assert 'name = "hello"' in conanfile
 
     def test_user_template_filenames(self):
-        client = TestClient()
+        client = TestClient(light=True)
         save(os.path.join(client.cache_folder, "templates/command/new/mytemplate/{{name}}"), "Hi!")
         client.run(f"new mytemplate -d name=pkg.txt")
         assert "Hi!" == client.load("pkg.txt")
 
     def test_skip_files(self):
-        client = TestClient()
+        client = TestClient(light=True)
         template1 = textwrap.dedent("""
             class Conan(ConanFile):
                 name = "{{name}}"
@@ -121,7 +121,7 @@ class TestNewCommandUserTemplate:
     def test_template_image_files(self):
         """ problematic files that we dont want to render with Jinja, like PNG or other binaries,
         have to be explicitly excluded from render"""
-        client = TestClient()
+        client = TestClient(light=True)
         template_dir = "templates/command/new/t_dir"
         png = "$&(){}{}{{}{}"
         save(os.path.join(client.cache_folder, template_dir, "myimage.png"), png)
@@ -137,12 +137,12 @@ class TestNewCommandUserTemplate:
 
 class TestNewErrors:
     def test_template_errors(self):
-        client = TestClient()
+        client = TestClient(light=True)
         client.run("new mytemplate", assert_error=True)
         assert "ERROR: Template doesn't exist" in client.out
 
     def test_forced(self):
-        client = TestClient()
+        client = TestClient(light=True)
         client.run("new cmake_lib -d name=hello -d version=0.1")
         client.run("new cmake_lib -d name=hello -d version=0.1", assert_error=True)
         client.run("new cmake_lib -d name=bye -d version=0.2 --force")
@@ -151,10 +151,20 @@ class TestNewErrors:
         assert 'version = "0.2"' in conanfile
 
     def test_duplicated(self):
-        client = TestClient()
+        client = TestClient(light=True)
         client.run("new cmake_lib -d name=hello -d name=0.1", assert_error=True)
         assert "ERROR: name argument can't be multiple: ['hello', '0.1']" in client.out
 
         # It will create a list and assign to it, but it will not fail ugly
         client.run("new cmake_lib -d name=pkg -d version=0.1 -d version=0.2", assert_error=True)
         assert "ERROR: version argument can't be multiple: ['0.1', '0.2']" in client.out
+
+    def test_name_uppercase(self):
+        client = TestClient(light=True)
+        client.run("new cmake_lib -d name=Hello", assert_error=True)
+        assert "ERROR: name argument must be lowercase: Hello" in client.out
+
+    def test_new_change_folder(self):
+        client = TestClient(light=True)
+        client.run("new cmake_lib -d name=hello -d version=0.1 -o=myfolder")
+        assert os.path.exists(os.path.join(client.current_folder, "myfolder", "conanfile.py"))
diff --git a/test/integration/command/upload/upload_test.py b/test/integration/command/upload/upload_test.py
index 926d0dce7..ab57218c2 100644
--- a/test/integration/command/upload/upload_test.py
+++ b/test/integration/command/upload/upload_test.py
@@ -2,6 +2,7 @@ import json
 import os
 import platform
 import stat
+import textwrap
 import unittest
 from collections import OrderedDict
 
@@ -15,7 +16,7 @@ from conans.model.package_ref import PkgReference
 from conans.model.recipe_ref import RecipeReference
 from conan.internal.paths import EXPORT_SOURCES_TGZ_NAME, PACKAGE_TGZ_NAME
 from conan.test.utils.tools import NO_SETTINGS_PACKAGE_ID, TestClient, TestServer, \
-    TurboTestClient, GenConanfile, TestRequester, TestingResponse
+    GenConanfile, TestRequester, TestingResponse
 from conans.util.files import gzopen_without_timestamps, is_dirty, save, set_dirty
 
 conanfile = """from conan import ConanFile
@@ -54,29 +55,36 @@ class UploadTest(unittest.TestCase):
 
     @pytest.mark.artifactory_ready
     def test_upload_force(self):
-        ref = RecipeReference.loads("hello/0.1@conan/testing")
-        client = TurboTestClient(default_server_user=True)
-        pref = client.create(ref, conanfile=GenConanfile().with_package_file("myfile.sh", "foo"))
+        client = TestClient(default_server_user=True)
+        conanfile_ = textwrap.dedent("""
+            from conan import ConanFile
+            from conan.tools.files import copy
+            class MyPkg(ConanFile):
+                name = "hello"
+                version = "0.1"
+                def package(self):
+                    copy(self, "myfile.sh", src=self.source_folder, dst=self.package_folder)
+            """)
+        client.save({"conanfile.py": conanfile_,
+                    "myfile.sh": "foo"})
+
+        client.run("export-pkg .")
         client.run("upload * --confirm -r default")
         assert "Uploading package 'hello" in client.out
         client.run("upload * --confirm -r default")
         assert "Uploading package" not in client.out
 
-        package_folder = client.get_latest_pkg_layout(pref).package()
-        package_file_path = os.path.join(package_folder, "myfile.sh")
-
         if platform.system() == "Linux":
-            client.run("remove '*' -c")
-            client.create(ref, conanfile=GenConanfile().with_package_file("myfile.sh", "foo"))
-            package_folder = client.get_latest_pkg_layout(pref).package()
-            package_file_path = os.path.join(package_folder, "myfile.sh")
+            package_file_path = os.path.join(client.current_folder, "myfile.sh")
             os.system('chmod +x "{}"'.format(package_file_path))
-            self.assertTrue(os.stat(package_file_path).st_mode & stat.S_IXUSR)
+            assert os.stat(package_file_path).st_mode & stat.S_IXUSR
+            client.run("export-pkg .")
+
             client.run("upload * --confirm -r default")
             # Doesn't change revision, doesn't reupload
-            self.assertNotIn("-> conan_package.tgz", client.out)
-            self.assertIn("skipping upload", client.out)
-            self.assertNotIn("Compressing package...", client.out)
+            assert "conan_package.tgz" not in client.out
+            assert "skipping upload" in client.out
+            assert "Compressing package..." not in client.out
 
         # with --force it really re-uploads it
         client.run("upload * --confirm --force -r default")
@@ -85,11 +93,11 @@ class UploadTest(unittest.TestCase):
 
         if platform.system() == "Linux":
             client.run("remove '*' -c")
-            client.run("install --requires={}".format(ref))
-            package_folder = client.get_latest_pkg_layout(pref).package()
-            package_file_path = os.path.join(package_folder, "myfile.sh")
+            client.run("install --requires=hello/0.1 --deployer=full_deploy")
+            package_file_path = os.path.join(client.current_folder, "full_deploy", "host", "hello",
+                                             "0.1", "myfile.sh")
             # Owner with execute permissions
-            self.assertTrue(os.stat(package_file_path).st_mode & stat.S_IXUSR)
+            assert os.stat(package_file_path).st_mode & stat.S_IXUSR
 
     @pytest.mark.artifactory_ready
     def test_pattern_upload(self):
@@ -395,24 +403,6 @@ class UploadTest(unittest.TestCase):
         client.run("upload hello0/1.2.1@user/testing -r server2")
         self.assertNotIn("ERROR: 'server1'", client.out)
 
-    def test_concurrent_upload(self):
-        # https://github.com/conan-io/conan/issues/4953
-        server = TestServer()
-        servers = OrderedDict([("default", server)])
-        client = TurboTestClient(servers=servers, inputs=["admin", "password"])
-        client2 = TurboTestClient(servers=servers, inputs=["admin", "password"])
-
-        ref = RecipeReference.loads("lib/1.0@conan/testing")
-        client.create(ref)
-        rrev = client.exported_recipe_revision()
-        client.upload_all(ref)
-        # Upload same with client2
-        client2.create(ref)
-        client2.run("upload lib/1.0@conan/testing -r default")
-        self.assertIn(f"'lib/1.0@conan/testing#{rrev}' already in "
-                      "server, skipping upload", client2.out)
-        self.assertNotIn("WARN", client2.out)
-
     def test_upload_without_user_channel(self):
         server = TestServer(users={"user": "password"}, write_permissions=[("*/*@*/*", "*")])
         servers = {"default": server}
diff --git a/test/integration/command_v2/list_test.py b/test/integration/command_v2/list_test.py
index a23bdeee6..008a6a9e3 100644
--- a/test/integration/command_v2/list_test.py
+++ b/test/integration/command_v2/list_test.py
@@ -18,20 +18,22 @@ from conans.util.files import load, save
 
 class TestParamErrors:
 
-    def test_query_param_is_required(self):
+    def test_default_pattern(self):
         c = TestClient()
-        c.run("list", assert_error=True)
-        assert "ERROR: Missing pattern or graph json file" in c.out
+        c.run("list")
+        assert "Found 0 pkg/version" in c.out
 
-        c.run("list -c", assert_error=True)
-        assert "ERROR: Missing pattern or graph json file" in c.out
+        c.run("list -c")
+        assert "Found 0 pkg/version" in c.out
 
         c.run('list -r="*"', assert_error=True)
-        assert "ERROR: Missing pattern or graph json file" in c.out
+        assert "ERROR: Remotes for pattern '*' can't be found" in c.out
 
         c.run("list --remote remote1 --cache", assert_error=True)
-        assert "ERROR: Missing pattern or graph json file" in c.out
+        assert "ERROR: Remote 'remote1' can't be found or is disabled" in c.out
 
+    def test_query_param(self):
+        c = TestClient()
         c.run("list * --graph=myjson", assert_error=True)
         assert "ERROR: Cannot define both the pattern and the graph json file" in c.out
 
diff --git a/test/integration/conan_api/list_test.py b/test/integration/conan_api/list_test.py
index 6e3c68c83..ee7ca6a53 100644
--- a/test/integration/conan_api/list_test.py
+++ b/test/integration/conan_api/list_test.py
@@ -1,75 +1,55 @@
-import copy
-
-
 from conan.api.conan_api import ConanAPI
+from conan.test.utils.env import environment_update
+from conans.model.package_ref import PkgReference
 from conans.model.recipe_ref import RecipeReference
 from conan.test.assets.genconanfile import GenConanfile
-from conan.test.utils.tools import TurboTestClient, TestClient
-
-
-def _pref_gen():
-    """
-    With the same recipe, every time the package is built the package revision will be different
-    """
-    package_lines = 'save(self, os.path.join(self.package_folder, "foo.txt"), str(time.time()))'
-    gen = GenConanfile().with_package(package_lines).with_import("import os, time") \
-        .with_import("from conan.tools.files import save")
-    return gen
+from conan.test.utils.tools import TestClient
 
 
 def test_get_recipe_revisions():
     """
     Test the "api.list.recipe_revisions"
     """
-    client = TurboTestClient(default_server_user=True)
-    ref = RecipeReference.loads("foo/1.0")
-    pref1 = client.create(ref, GenConanfile().with_build_msg("change1"))
-    pref2 = client.create(ref, GenConanfile().with_build_msg("change2"))
-    pref3 = client.create(ref, GenConanfile().with_build_msg("change3"))
-    client.upload_all(pref1.ref, "default")
-    client.upload_all(pref2.ref, "default")
-    client.upload_all(pref3.ref, "default")
+    client = TestClient(default_server_user=True)
+    for rev in range(1, 4):
+        client.save({"conanfile.py": GenConanfile("foo", "1.0").with_build_msg(f"{rev}")})
+        client.run("create .")
+    client.run("upload * -r=default -c")
 
     api = ConanAPI(client.cache_folder)
-
     # Check the revisions locally
+    ref = RecipeReference.loads("foo/1.0")
     sot = api.list.recipe_revisions(ref)
-    assert sot == [pref3.ref, pref2.ref, pref1.ref]
-
-    # Check the revisions in the remote
-    with client.mocked_servers():
-        sot = api.list.recipe_revisions(ref, api.remotes.get("default"))
-        assert sot == [pref3.ref, pref2.ref, pref1.ref]
+    sot = [r.repr_notime() for r in sot]
+    assert ["foo/1.0#6707ddcdb444fd46f92d449d11700c5a",
+            "foo/1.0#913d984a1b9b8a2821d8c4d4e9cf8d57",
+            "foo/1.0#b87cdb893042ec4b371bc6aa82a0108f"] == sot
 
 
 def test_get_package_revisions():
     """
     Test the "api.list.package_revisions"
     """
-    client = TurboTestClient(default_server_user=True)
-    ref = RecipeReference.loads("foo/1.0")
-    gen = _pref_gen()
-    # FIXME: The upload only takes the latest package revision, so do the upload after creating it
-    pref1 = client.create(ref, gen)
-    client.upload_all(pref1.ref, "default")
-    pref2 = client.create(ref, gen)
-    client.upload_all(pref1.ref, "default")
-    pref3 = client.create(ref, gen)
-    client.upload_all(pref1.ref, "default")
-
-    _pref = copy.copy(pref1)
-    _pref.revision = None
+    client = TestClient(default_server_user=True)
+    client.save({"conanfile.py": GenConanfile("foo", "1.0").with_package_file("f.txt",
+                                                                              env_var="MYVAR")})
+    for rev in range(3):
+        with environment_update({"MYVAR": f"{rev}"}):
+            client.run("create . ")
+    client.run("upload * -r=default -c")
 
     api = ConanAPI(client.cache_folder)
-
     # Check the revisions locally
-    sot = api.list.package_revisions(_pref)
-    assert sot == [pref3, pref2, pref1]
-
-    # Check the revisions in the remote
-    with client.mocked_servers():
-        sot = api.list.package_revisions(_pref, api.remotes.get("default"))
-        assert sot == [pref3, pref2, pref1]
+    pref = PkgReference.loads("foo/1.0#77ead28a5fd4216349b5b2181f4d32d4:"
+                              "da39a3ee5e6b4b0d3255bfef95601890afd80709")
+    sot = api.list.package_revisions(pref)
+    sot = [r.repr_notime() for r in sot]
+    assert ['foo/1.0#77ead28a5fd4216349b5b2181f4d32d4:'
+            'da39a3ee5e6b4b0d3255bfef95601890afd80709#8b34fcf0543672de78ce1fe4f7fb3daa',
+            'foo/1.0#77ead28a5fd4216349b5b2181f4d32d4:'
+            'da39a3ee5e6b4b0d3255bfef95601890afd80709#5bb077c148d587da50ce9c3212370b5d',
+            'foo/1.0#77ead28a5fd4216349b5b2181f4d32d4:'
+            'da39a3ee5e6b4b0d3255bfef95601890afd80709#370f42f40d3f353a83a0f529ba2be1ce'] == sot
 
 
 def test_search_recipes_no_user_channel_only():
diff --git a/test/integration/conanfile/conanfile_errors_test.py b/test/integration/conanfile/conanfile_errors_test.py
index c10dc1c0b..5c59e782d 100644
--- a/test/integration/conanfile/conanfile_errors_test.py
+++ b/test/integration/conanfile/conanfile_errors_test.py
@@ -3,6 +3,7 @@ import unittest
 
 import pytest
 
+from conan.test.assets.genconanfile import GenConanfile
 from conan.test.utils.tools import TestClient
 
 
@@ -216,3 +217,26 @@ def test_shorthand_bad_interface(property_name, property_content):
     else:
         assert f"The {property_name} property is undefined because {property_name}s is empty." in c.out
 
+
+def test_consumer_unexpected():
+    tc = TestClient(light=True)
+    cmake_conanfile = textwrap.dedent("""
+    from conan import ConanFile
+
+    class CMake(ConanFile):
+        name = "cmake"
+        version = "1.0"
+        package_type = "application"
+
+        def package_info(self):
+            self.output.info("cmake/1.0 -> " + str(self._conan_is_consumer))
+    """)
+    tc.save({
+             "cmake/conanfile.py": cmake_conanfile,
+             "dep1/conanfile.py": GenConanfile("dep1", "1.0"),
+             "conanfile.py": GenConanfile("pkg", "1.0").with_requires("dep1/1.0"),
+             "profile": "include(default)\n[tool_requires]\n*: cmake/1.0\n"})
+    tc.run("export dep1")
+    tc.run("create cmake")
+    tc.run("create . -b=missing -pr=profile")
+    assert "cmake/1.0 -> True" not in tc.out
diff --git a/test/integration/configuration/profile_test.py b/test/integration/configuration/profile_test.py
index 37a3fbab7..dc7e6ab49 100644
--- a/test/integration/configuration/profile_test.py
+++ b/test/integration/configuration/profile_test.py
@@ -557,6 +557,30 @@ def test_create_and_priority_of_consumer_specific_setting():
     assert "I'm None and my build type is Debug" in client.out
 
 
+def test_package_consumer_is_only_the_tested_one():
+    """
+    the &:xxx pattern applies only to the package being tested, not other requires
+    """
+    c = TestClient(light=True)
+    test = textwrap.dedent("""
+        from conan import ConanFile
+        class Tool(ConanFile):
+            def requirements(self):
+                self.requires(self.tested_reference_str)
+                self.requires("dep/1.0")
+
+            def test(self):
+                pass
+        """)
+    c.save({"dep/conanfile.py": GenConanfile("dep", "1.0"),
+            "pkg/conanfile.py": GenConanfile("pkg", "0.1").with_option("myoption", [1, 2]),
+            "pkg/test_package/conanfile.py": test})
+    c.run("create dep")
+    c.run("create pkg -o &:myoption=1")
+    # This would crash if myoption is applied to dep
+    assert 'pkg/0.1 (test package): Running test()' in c.out
+
+
 def test_consumer_specific_settings_from_profile():
     client = TestClient()
     conanfile = str(GenConanfile().with_settings("build_type").with_name("hello"))
diff --git a/test/integration/cross_building/build_requires_from_profile_test.py b/test/integration/cross_building/build_requires_from_profile_test.py
index 32646e200..48a8c6883 100644
--- a/test/integration/cross_building/build_requires_from_profile_test.py
+++ b/test/integration/cross_building/build_requires_from_profile_test.py
@@ -1,3 +1,4 @@
+import os
 import unittest
 import textwrap
 
@@ -260,3 +261,31 @@ class BuildRequiresBothContextsTest(unittest.TestCase):
         self.assertIn("gtest/1.0: Build OS=Linux", t.out)
         self.assertIn("gtest/1.0: PackageInfo OS=Linux", t.out)
         self.assertIn("library/version: Build OS=Linux", t.out)
+
+
+def test_consumer_get_profile_tool_requires():
+    # [tool_requires] defined in a profile will result in applying them in the
+    # consumer, both if using a conanfile.txt of using a --requires
+    t = TestClient(light=True)
+    tool = textwrap.dedent("""
+        from conan import ConanFile
+        class Pkg(ConanFile):
+            name = "tool"
+            version = "1.0"
+            def package_info(self):
+                self.buildenv_info.define("MYVAR", "MYVALUE")
+        """)
+    t.save({'profile': "[tool_requires]\ntool/1.0",
+            'dep.py': GenConanfile("dep", "1.0"),
+            'tool.py': tool,
+            'conanfile.txt': ""})
+    t.run("create tool.py")
+    t.run("create dep.py")
+    t.run("install . --profile ./profile")
+    env = t.load("conanbuildenv.sh")
+    assert 'export MYVAR="MYVALUE"' in env
+    os.remove(os.path.join(t.current_folder, "conanbuildenv.sh"))
+    # also when using --requires
+    t.run("install --requires=dep/1.0 --profile ./profile")
+    env = t.load("conanbuildenv.sh")
+    assert 'export MYVAR="MYVALUE"' in env
diff --git a/test/integration/editable/transitive_editable_test.py b/test/integration/editable/transitive_editable_test.py
index 96113b84d..bb37efab4 100644
--- a/test/integration/editable/transitive_editable_test.py
+++ b/test/integration/editable/transitive_editable_test.py
@@ -5,7 +5,7 @@ from conan.test.utils.tools import TestClient, GenConanfile
 
 def test_transitive_editables_half_diamond():
     # https://github.com/conan-io/conan/issues/4445
-    client = TestClient()
+    client = TestClient(light=True)
     client.save({"libc/conanfile.py": GenConanfile("libc", "0.1"),
                  "libb/conanfile.py": GenConanfile("libb", "0.1").with_require("libc/0.1"),
                  "liba/conanfile.py": GenConanfile("liba", "0.1").with_requires("libb/0.1",
@@ -78,7 +78,7 @@ def test_transitive_editable_test_requires():
 
 def test_transitive_editables_python_requires_version_range():
     # https://github.com/conan-io/conan/issues/14411
-    client = TestClient(default_server_user=True)
+    client = TestClient(default_server_user=True, light=True)
     client.save({"dep/conanfile.py": GenConanfile("dep", "0.1"),
                  "pkg/conanfile.py": GenConanfile("pkg", "0.1").with_python_requires("dep/[*]")})
     client.run("editable add pkg", assert_error=True)
@@ -124,7 +124,7 @@ def test_transitive_editable_cascade_build():
     """
     https://github.com/conan-io/conan/issues/15292
     """
-    c = TestClient()
+    c = TestClient(light=True)
     pkga = GenConanfile("pkga", "1.0").with_package_type("static-library")
     pkgb = GenConanfile("pkgb", "1.0").with_requires("pkga/1.0").with_package_type("static-library")
     pkgc = GenConanfile("pkgc", "1.0").with_requires("pkgb/1.0").with_package_type("static-library")
@@ -168,7 +168,7 @@ def test_transitive_editable_cascade_package_id():
     """
     https://github.com/conan-io/conan/issues/15292
     """
-    c = TestClient()
+    c = TestClient(light=True)
     pkga = GenConanfile("pkga", "1.0").with_package_type("static-library")
     pkgb = GenConanfile("pkgb", "1.0").with_requires("pkga/1.0").with_package_type("static-library")
     pkgc = GenConanfile("pkgc", "1.0").with_requires("pkgb/1.0").with_package_type("shared-library")
@@ -207,3 +207,18 @@ def test_transitive_editable_cascade_package_id():
     # The consumers didn't need a new binary, even if I modified pkg
     c.assert_listed_binary({"pkgb/1.0": (pkgb_id, "Cache"),
                             "pkgc/1.0": (pkgc_id, "Build")})
+
+
+def test_warning_from_cache():
+    c = TestClient(light=True)
+    c.save({"pkga/conanfile.py":  GenConanfile("pkga", "1.0"),
+            "pkgb/conanfile.py": GenConanfile("pkgb", "1.0").with_requires("pkga/1.0"),
+            "pkgc/conanfile.py": GenConanfile("pkgc", "1.0").with_requires("pkgb/1.0")})
+    c.run("editable add pkga")
+    c.run("create pkgb")
+    assert "pkgb/1.0: WARN: risk: Package is being built in the cache using editable" in c.out
+    c.run("create pkgc")
+    assert "pkgc/1.0: WARN: risk: Package is being built in the cache using editable" in c.out
+    c.run("create pkgc --build=*")
+    assert "pkgb/1.0: WARN: risk: Package is being built in the cache using editable" in c.out
+    assert "pkgc/1.0: WARN: risk: Package is being built in the cache using editable" in c.out
diff --git a/test/integration/graph/test_replace_requires.py b/test/integration/graph/test_replace_requires.py
index ae615c9bb..7f9d4bdd4 100644
--- a/test/integration/graph/test_replace_requires.py
+++ b/test/integration/graph/test_replace_requires.py
@@ -279,3 +279,39 @@ def test_replace_requires_consumer_components_options():
     assert "target_link_libraries(... ZLIB::ZLIB)" in c.out
     assert "zlib in deps?: True" in c.out
     assert "zlib-ng in deps?: False" in c.out
+
+
+def test_replace_requires_multiple():
+    conanfile = textwrap.dedent("""
+        from conan import ConanFile
+
+        class EpoxyConan(ConanFile):
+            name = "libepoxy"
+            version = "0.1"
+
+            def requirements(self):
+                self.requires("opengl/system")
+                self.requires("egl/system")
+
+            def generate(self):
+                for r, d in self.dependencies.items():
+                    self.output.info(f"DEP: {r.ref.name}: {d.ref.name}")
+
+            def package_info(self):
+                self.cpp_info.requires.append("opengl::opengl")
+                self.cpp_info.requires.append("egl::egl")
+        """)
+    profile = textwrap.dedent("""
+        [replace_requires]
+        opengl/system: libgl/1.0
+        egl/system: libgl/1.0
+        """)
+    c = TestClient()
+    c.save({"dep/conanfile.py": GenConanfile(),
+            "app/conanfile.py": conanfile,
+            "profile": profile})
+    c.run("create dep --name=libgl --version=1.0")
+    c.run("create app -pr=profile")
+    # There are actually 2 dependencies, pointing to the same node
+    assert "libepoxy/0.1: DEP: opengl: libgl" in c.out
+    assert "libepoxy/0.1: DEP: egl: libgl" in c.out
diff --git a/test/integration/lockfile/test_ci.py b/test/integration/lockfile/test_ci.py
index e8bbd4a22..542dbd47e 100644
--- a/test/integration/lockfile/test_ci.py
+++ b/test/integration/lockfile/test_ci.py
@@ -202,7 +202,7 @@ def test_single_config_centralized_change_dep(client_setup):
     c.run("install --requires=app1/0.1@  --lockfile=app1_b_changed.lock "
           "--lockfile-out=app1_b_integrated.lock "
           "--build=missing  -s os=Windows")
-    assert "pkga" not in c.out
+    assert "pkga/" not in c.out
     c.assert_listed_binary({"pkgj/0.1": (pkgawin_01_id, "Cache"),
                             "pkgb/0.2": ("79caa65bc5877c4ada84a2b454775f47a5045d59", "Cache"),
                             "pkgc/0.1": ("67e4e9b17f41a4c71ff449eb29eb716b8f83767b", "Build"),
diff --git a/test/integration/lockfile/test_ci_revisions.py b/test/integration/lockfile/test_ci_revisions.py
index 6fa1930c3..12952fc0d 100644
--- a/test/integration/lockfile/test_ci_revisions.py
+++ b/test/integration/lockfile/test_ci_revisions.py
@@ -208,7 +208,7 @@ def test_single_config_centralized_change_dep(client_setup):
     c.run("install --requires=app1/0.1@  --lockfile=app1_b_changed.lock "
           "--lockfile-out=app1_b_integrated.lock "
           "--build=missing  -s os=Windows")
-    assert "pkga" not in c.out
+    assert "pkga/" not in c.out
     c.assert_listed_binary({"pkgj/0.1": (pkgawin_01_id, "Cache"),
                             "pkgb/0.1": ("6142fb85ccd4e94afad85a8d01a87234eefa5600", "Cache"),
                             "pkgc/0.1": ("93cfcbc8109eedf4211558258ff5a844fdb62cca", "Build"),
diff --git a/test/integration/options/options_test.py b/test/integration/options/options_test.py
index 8aedbbfb1..aac9dbc9c 100644
--- a/test/integration/options/options_test.py
+++ b/test/integration/options/options_test.py
@@ -744,11 +744,16 @@ class TestImportantOptions:
 
 class TestConflictOptionsWarnings:
 
-    def test_options_warnings(self):
+    @pytest.mark.parametrize("important", [True, False])
+    def test_options_warnings(self, important):
         c = TestClient()
         liba = GenConanfile("liba", "0.1").with_option("myoption", [1, 2, 3], default=1)
         libb = GenConanfile("libb", "0.1").with_requires("liba/0.1")
-        libc = GenConanfile("libc", "0.1").with_requirement("liba/0.1", options={"myoption": 2})
+        if important:
+            libc = GenConanfile("libc", "0.1").with_requirement("liba/0.1") \
+                                              .with_default_option("liba*:myoption!", 2)
+        else:
+            libc = GenConanfile("libc", "0.1").with_requirement("liba/0.1", options={"myoption": 2})
         app = GenConanfile().with_requires("libb/0.1", "libc/0.1")
 
         c.save({"liba/conanfile.py": liba,
@@ -767,3 +772,4 @@ class TestConflictOptionsWarnings:
                 It is recommended to define options values in profiles, not in recipes
             """)
         assert expected in c.out
+        assert "WARN: risk: There are options conflicts in the dependency graph" in c.out
diff --git a/test/integration/symlinks/symlinks_test.py b/test/integration/symlinks/symlinks_test.py
index 2ceb7bd8b..48cbe16ff 100644
--- a/test/integration/symlinks/symlinks_test.py
+++ b/test/integration/symlinks/symlinks_test.py
@@ -4,11 +4,12 @@ import textwrap
 
 import pytest
 
+from conans.model.package_ref import PkgReference
 from conans.model.recipe_ref import RecipeReference
 from conan.internal.paths import PACKAGE_TGZ_NAME
 from conan.test.assets.genconanfile import GenConanfile
 from conan.test.utils.test_files import temp_folder
-from conan.test.utils.tools import TestClient, TestServer, TurboTestClient
+from conan.test.utils.tools import TestClient, TestServer
 from conans.util.files import load, rmdir, chdir, save
 
 links_conanfile = textwrap.dedent("""
@@ -193,8 +194,8 @@ def test_complete_round_trip_folders():
                           ])
 def test_package_with_symlinks(package_files):
 
-    client = TurboTestClient(default_server_user=True)
-    client2 = TurboTestClient(servers=client.servers)
+    client = TestClient(default_server_user=True)
+    client2 = TestClient(servers=client.servers)
     client.save({"conanfile.py": links_conanfile})
 
     for path in package_files["files"]:
@@ -203,7 +204,7 @@ def test_package_with_symlinks(package_files):
     for link_dest, link_file in package_files["symlinks"]:
         os.symlink(link_dest, os.path.join(client.current_folder, link_file))
 
-    pref = client.create(RecipeReference.loads("hello/0.1"), conanfile=False)
+    client.run("create .")
 
     def assert_folder_symlinks(base_folder):
         with chdir(base_folder):
@@ -215,12 +216,12 @@ def test_package_with_symlinks(package_files):
                     assert load(link) == "foo contents"
 
     # Check exported sources are there
-    ref_layout = client.get_latest_ref_layout(pref.ref)
+    ref_layout = client.exported_layout()
     assert_folder_symlinks(ref_layout.export_sources())
     assert_folder_symlinks(ref_layout.source())
 
     # Check files have been copied to the build
-    pkg_layout = client.get_latest_pkg_layout(pref)
+    pkg_layout = client.created_layout()
     assert_folder_symlinks(pkg_layout.build())
     assert_folder_symlinks(pkg_layout.package())
 
@@ -229,6 +230,9 @@ def test_package_with_symlinks(package_files):
 
     # Client 2 install
     client2.run("install --requires=hello/0.1 --deployer=full_deploy")
+    ref = client2.get_latest_ref_layout(RecipeReference.loads("hello/0.1"))
+    pref = PkgReference(ref.reference, "da39a3ee5e6b4b0d3255bfef95601890afd80709")
+
     # Check package files are there
     package_folder = client2.get_latest_pkg_layout(pref).package()
     assert_folder_symlinks(package_folder)
@@ -240,25 +244,25 @@ def test_exports_does_not_follow_symlink():
     tmp = temp_folder()
     linked_abs_folder = tmp
     save(os.path.join(tmp, "source.cpp"), "foo")
-    client = TurboTestClient(default_server_user=True)
-    conanfile = GenConanfile()\
+    client = TestClient(default_server_user=True)
+    conanfile = GenConanfile("lib", "1.0")\
         .with_package('copy(self, "*", self.source_folder, self.package_folder)')\
         .with_exports_sources("*")\
         .with_import("from conan.tools.files import copy")
     client.save({"conanfile.py": conanfile, "foo.txt": "bar"})
     os.symlink(linked_abs_folder, os.path.join(client.current_folder, "linked_folder"))
-    pref = client.create(RecipeReference.loads("lib/1.0"), conanfile=False)
-    exports_sources_folder = client.get_latest_ref_layout(pref.ref).export_sources()
+    client.run("create . ")
+    exports_sources_folder = client.exported_layout().export_sources()
     assert os.path.islink(os.path.join(exports_sources_folder, "linked_folder"))
     assert os.path.exists(os.path.join(exports_sources_folder, "linked_folder", "source.cpp"))
 
     # Check files have been copied to the build
-    build_folder = client.get_latest_pkg_layout(pref).build()
+    build_folder = client.created_layout().build()
     assert os.path.islink(os.path.join(build_folder, "linked_folder"))
     assert os.path.exists(os.path.join(build_folder, "linked_folder", "source.cpp"))
 
     # Check package files are there
-    package_folder = client.get_latest_pkg_layout(pref).package()
+    package_folder = client.created_layout().package()
     assert os.path.islink(os.path.join(package_folder, "linked_folder"))
     assert os.path.exists(os.path.join(package_folder, "linked_folder", "source.cpp"))
 
@@ -278,7 +282,7 @@ def test_exports_does_not_follow_symlink():
 @pytest.mark.skipif(platform.system() != "Linux", reason="Only linux")
 def test_package_symlinks_zero_size():
     server = TestServer()
-    client = TurboTestClient(servers={"default": server}, inputs=["admin", "password"])
+    client = TestClient(servers={"default": server}, inputs=["admin", "password"])
 
     conanfile = """
 import os
@@ -286,20 +290,22 @@ from conan import ConanFile
 from conan.tools.files import save
 
 class HelloConan(ConanFile):
+    name = "hello"
+    version = "0.1"
 
     def package(self):
         # Link to file.txt and then remove it
         save(self, os.path.join(self.package_folder, "file.txt"), "contents")
         os.symlink("file.txt", os.path.join(self.package_folder, "link.txt"))
 """
-    ref = RecipeReference.loads("lib/1.0@conan/stable")
+    client.save({"conanfile.py": conanfile})
     # By default it is not allowed
-    pref = client.create(ref, conanfile=conanfile)
+    client.run("create .")
+    p_folder = client.created_layout().download_package()
     # Upload, it will create the tgz
-    client.upload_all(ref)
+    client.run("upload * -r=default -c")
 
     # We can uncompress it without warns
-    p_folder = client.get_latest_pkg_layout(pref).download_package()
     tgz = os.path.join(p_folder, PACKAGE_TGZ_NAME)
     client.run_command('gzip -d "{}"'.format(tgz))
     client.run_command('tar tvf "{}"'.format(os.path.join(p_folder, "conan_package.tar")))
diff --git a/test/integration/toolchains/google/test_bazeldeps.py b/test/integration/toolchains/google/test_bazeldeps.py
index a9a0b3d78..de31863da 100644
--- a/test/integration/toolchains/google/test_bazeldeps.py
+++ b/test/integration/toolchains/google/test_bazeldeps.py
@@ -679,9 +679,15 @@ def test_tool_requires():
     client = TestClient()
     conanfile = textwrap.dedent("""
         from conan import ConanFile
+        from conan.tools.files import save
 
         class PkgBazelConan(ConanFile):
 
+            def package(self):
+                import os
+                libdirs = os.path.join(self.package_folder, "lib")
+                save(self, os.path.join(libdirs, "libtool.lib"), "")
+
             def package_info(self):
                 self.cpp_info.libs = ["libtool"]
         """)
@@ -690,9 +696,16 @@ def test_tool_requires():
 
     conanfile = textwrap.dedent("""
         from conan import ConanFile
+        from conan.tools.files import save
 
         class PkgBazelConan(ConanFile):
 
+            def package(self):
+                import os
+                libdirs = os.path.join(self.package_folder, "lib")
+                save(self, os.path.join(libdirs, "other_cmp1.lib"), "")
+                save(self, os.path.join(libdirs, "other_cmp2.lib"), "")
+
             def package_info(self):
                 self.cpp_info.set_property("bazel_target_name", "libother")
                 self.cpp_info.set_property("bazel_repository_name", "other-repo")
@@ -726,6 +739,20 @@ def test_tool_requires():
     client.run("install . -pr:h default -pr:b default")
     assert 'name = "tool"' in client.load("build-tool/BUILD.bazel")
     assert textwrap.dedent("""\
+    # Components precompiled libs
+    cc_import(
+        name = "other_cmp1_precompiled",
+        static_library = "lib/other_cmp1.lib",
+    )
+
+    cc_import(
+        name = "other_cmp2_precompiled",
+        static_library = "lib/other_cmp2.lib",
+    )
+
+
+    # Root package precompiled libs
+
     # Components libraries declaration
     cc_library(
         name = "component1",
@@ -736,6 +763,10 @@ def test_tool_requires():
             "include",
         ],
         visibility = ["//visibility:public"],
+        deps = [
+            # do not sort
+            ":other_cmp1_precompiled",
+        ],
     )
 
     cc_library(
@@ -747,6 +778,10 @@ def test_tool_requires():
             "include",
         ],
         visibility = ["//visibility:public"],
+        deps = [
+            # do not sort
+            ":other_cmp2_precompiled",
+        ],
     )
 
     cc_library(
@@ -800,9 +835,14 @@ def test_tool_requires_not_created_if_no_activated():
     client = TestClient()
     conanfile = textwrap.dedent("""
         from conan import ConanFile
+        from conan.tools.files import save
 
         class PkgBazelConan(ConanFile):
 
+            def package(self):
+                import os
+                libdirs = os.path.join(self.package_folder, "lib")
+                save(self, os.path.join(libdirs, "libtool.lib"), "")
             def package_info(self):
                 self.cpp_info.libs = ["libtool"]
         """)
@@ -833,9 +873,14 @@ def test_tool_requires_raise_exception_if_exist_both_require_and_build_one():
     client = TestClient()
     conanfile = textwrap.dedent("""
         from conan import ConanFile
+        from conan.tools.files import save
 
         class PkgBazelConan(ConanFile):
 
+            def package(self):
+                import os
+                libdirs = os.path.join(self.package_folder, "lib")
+                save(self, os.path.join(libdirs, "libtool.lib"), "")
             def package_info(self):
                 self.cpp_info.libs = ["libtool"]
         """)
@@ -1137,7 +1182,7 @@ def test_shared_windows_find_libraries():
 
     cc_import(
         name = "libssl_precompiled",
-        shared_library = "bin/libcrypto-3-x64.dll",
+        shared_library = "bin/libssl-3-x64.dll",
         interface_library = "lib/libssl.lib",
     )
     """)
diff --git a/test/integration/workspace/__init__.py b/test/integration/workspace/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/test/integration/workspace/test_workspace.py b/test/integration/workspace/test_workspace.py
new file mode 100644
index 000000000..fc78c0847
--- /dev/null
+++ b/test/integration/workspace/test_workspace.py
@@ -0,0 +1,292 @@
+import json
+import os
+import textwrap
+
+import pytest
+
+from conan.internal.workspace import Workspace
+from conan.test.assets.genconanfile import GenConanfile
+from conan.test.utils.scm import create_local_git_repo
+from conan.test.utils.test_files import temp_folder
+from conan.test.utils.tools import TestClient
+from conans.util.files import save
+
+Workspace.TEST_ENABLED = "will_break_next"
+
+
+class TestHomeRoot:
+    @pytest.mark.parametrize("ext, content", [("py", "home_folder = 'myhome'"),
+                                              ("yml", "home_folder: myhome")])
+    def test_workspace_home(self, ext, content):
+        folder = temp_folder()
+        cwd = os.path.join(folder, "sub1", "sub2")
+        save(os.path.join(folder, f"conanws.{ext}"), content)
+        c = TestClient(current_folder=cwd, light=True)
+        c.run("config home")
+        assert os.path.join(folder, "myhome") in c.stdout
+
+    def test_workspace_home_user_py(self):
+        folder = temp_folder()
+        cwd = os.path.join(folder, "sub1", "sub2")
+        conanwspy = textwrap.dedent("""
+            def home_folder():
+                return "new" + conanws_data["home_folder"]
+            """)
+        save(os.path.join(folder, f"conanws.py"), conanwspy)
+        save(os.path.join(folder, "conanws.yml"), "home_folder: myhome")
+        c = TestClient(current_folder=cwd, light=True)
+        c.run("config home")
+        assert os.path.join(folder, "newmyhome") in c.stdout
+
+    def test_workspace_root(self):
+        c = TestClient(light=True)
+        # Just check the root command works
+        c.run("workspace root", assert_error=True)
+        assert "ERROR: No workspace defined, conanws.py file not found" in c.out
+        c.save({"conanws.py": ""})
+        c.run("workspace root")
+        assert c.current_folder in c.stdout
+
+        c.save({"conanws.yml": ""}, clean_first=True)
+        c.run("workspace root")
+        assert c.current_folder in c.stdout
+
+
+class TestAddRemove:
+
+    def test_add(self):
+        c = TestClient(light=True)
+        c.save({"conanws.py": "name='myws'",
+                "dep1/conanfile.py": GenConanfile("dep1", "0.1"),
+                "dep2/conanfile.py": GenConanfile("dep2", "0.1"),
+                "dep3/conanfile.py": GenConanfile("dep3", "0.1")})
+        c.run("workspace add dep1")
+        assert "Reference 'dep1/0.1' added to workspace" in c.out
+        c.run("workspace info")
+        assert "dep1/0.1" in c.out
+        assert "dep2" not in c.out
+        c.run("editable list")  # No editables in global
+        assert "dep1" not in c.out
+        assert "dep2" not in c.out
+        c.run("workspace add dep2")
+        assert "Reference 'dep2/0.1' added to workspace" in c.out
+        c.run("workspace info")
+        assert "dep1/0.1" in c.out
+        assert "dep2/0.1" in c.out
+
+        with c.chdir(temp_folder()):  # If we move to another folder, outside WS, no editables
+            c.run("editable list")
+            assert "dep1" not in c.out
+            assert "dep2" not in c.out
+
+        c.run("workspace info")
+        assert "dep1/0.1" in c.out
+        assert "dep2/0.1" in c.out
+
+        c.run("workspace remove dep1")
+        c.run("workspace info")
+        assert "dep1/0.1" not in c.out
+        assert "dep2/0.1" in c.out
+
+        c.run("workspace remove dep2")
+        c.run("workspace info")
+        assert "dep1/0.1" not in c.out
+        assert "dep2/0.1" not in c.out
+
+    def test_add_from_outside(self):
+        c = TestClient(light=True)
+        c.save({"sub/conanws.py": "name='myws'",
+                "sub/dep1/conanfile.py": GenConanfile("dep1", "0.1"),
+                "sub/dep2/conanfile.py": GenConanfile("dep2", "0.1")})
+        with c.chdir("sub"):
+            c.run("workspace add dep1")
+            assert "Reference 'dep1/0.1' added to workspace" in c.out
+            c.run("workspace add dep2")
+            assert "Reference 'dep2/0.1' added to workspace" in c.out
+            c.run("workspace info")
+            assert "dep1/0.1" in c.out
+            assert "dep2/0.1" in c.out
+        assert c.load_home("editable_packages.json") is None
+
+        c.run("editable list")
+        assert "dep1" not in c.out
+        assert "dep2" not in c.out
+        assert c.load_home("editable_packages.json") is None
+        with c.chdir("sub"):
+            c.run("editable add dep1")
+            assert c.load_home("editable_packages.json") is not None
+            c.run("editable list")
+            assert "dep1/0.1" in c.out
+            assert "dep2/0.1" not in c.out
+            c.run("workspace info")
+            assert "dep1" in c.out
+            assert "dep2" in c.out
+
+        c.run("editable list")
+        assert "dep1/0.1" in c.out
+        assert "dep2" not in c.out
+
+    @pytest.mark.parametrize("api", [False, True])
+    def test_dynamic_editables(self, api):
+        c = TestClient(light=True)
+        conanfile = textwrap.dedent("""
+            import os
+            from conan import ConanFile
+            from conan.tools.files import load
+            class Lib(ConanFile):
+                def set_name(self):
+                    self.name = load(self, os.path.join(self.recipe_folder, "name.txt"))
+                def set_version(self):
+                    self.version = load(self, os.path.join(self.recipe_folder, "version.txt"))
+            """)
+        if not api:
+            workspace = textwrap.dedent("""\
+                import os
+                name = "myws"
+
+                workspace_folder = os.path.dirname(os.path.abspath(__file__))
+
+                def editables():
+                    result = {}
+                    for f in os.listdir(workspace_folder):
+                        if os.path.isdir(os.path.join(workspace_folder, f)):
+                            name = open(os.path.join(workspace_folder, f, "name.txt")).read().strip()
+                            version = open(os.path.join(workspace_folder, f,
+                                                        "version.txt")).read().strip()
+                            p = os.path.join(f, "conanfile.py").replace("\\\\", "/")
+                            result[f"{name}/{version}"] = {"path": p}
+                    return result
+                """)
+        else:
+            workspace = textwrap.dedent("""\
+               import os
+               name = "myws"
+
+               def editables(*args, **kwargs):
+                   result = {}
+                   for f in os.listdir(workspace_api.folder):
+                       if os.path.isdir(os.path.join(workspace_api.folder, f)):
+                           f = os.path.join(f, "conanfile.py").replace("\\\\", "/")
+                           conanfile = workspace_api.load(f)
+                           result[f"{conanfile.name}/{conanfile.version}"] = {"path": f}
+                   return result
+               """)
+
+        c.save({"conanws.py": workspace,
+                "dep1/conanfile.py": conanfile,
+                "dep1/name.txt": "pkg",
+                "dep1/version.txt": "2.1"})
+        c.run("workspace info --format=json")
+        info = json.loads(c.stdout)
+        assert info["editables"] == {"pkg/2.1": {"path": "dep1/conanfile.py"}}
+        c.save({"dep1/name.txt": "other",
+                "dep1/version.txt": "14.5"})
+        c.run("workspace info --format=json")
+        info = json.loads(c.stdout)
+        assert info["editables"] == {"other/14.5": {"path": "dep1/conanfile.py"}}
+        c.run("install --requires=other/14.5")
+        # Doesn't fail
+        assert "other/14.5 - Editable" in c.out
+        with c.chdir("dep1"):
+            c.run("install --requires=other/14.5")
+            # Doesn't fail
+            assert "other/14.5 - Editable" in c.out
+
+    def test_error_uppercase(self):
+        c = TestClient(light=True)
+        c.save({"conanws.py": "name='myws'",
+                "conanfile.py": GenConanfile("Pkg", "0.1")})
+        c.run("workspace add .", assert_error=True)
+        assert "ERROR: Conan packages names 'Pkg/0.1' must be all lowercase" in c.out
+        c.save({"conanfile.py": GenConanfile()})
+        c.run("workspace add . --name=Pkg --version=0.1", assert_error=True)
+        assert "ERROR: Conan packages names 'Pkg/0.1' must be all lowercase" in c.out
+
+    def test_add_open_error(self):
+        c = TestClient(light=True)
+        c.save({"conanws.py": "name='myws'",
+                "dep/conanfile.py": GenConanfile("dep", "0.1")})
+        c.run("workspace add dep")
+        c.run("workspace open dep/0.1", assert_error=True)
+        assert "ERROR: Can't open a dependency that is already an editable: dep/0.1" in c.out
+
+
+class TestOpenAdd:
+    def test_without_git(self):
+        t = TestClient(default_server_user=True, light=True)
+        t.save({"conanfile.py": GenConanfile("pkg", "0.1")})
+        t.run("create .")
+        t.run("upload * -r=default -c")
+
+        c = TestClient(servers=t.servers, light=True)
+        c.run(f"workspace open pkg/0.1")
+        assert "name = 'pkg'" in c.load("pkg/conanfile.py")
+
+        # The add should work the same
+        c2 = TestClient(servers=t.servers, light=True)
+        c2.save({"conanws.py": ""})
+        c2.run(f"workspace add --ref=pkg/0.1")
+        assert "name = 'pkg'" in c2.load("pkg/conanfile.py")
+        c2.run("workspace info")
+        assert "pkg/0.1" in c2.out
+
+    def test_without_git_export_sources(self):
+        t = TestClient(default_server_user=True, light=True)
+        t.save({"conanfile.py": GenConanfile("pkg", "0.1").with_exports_sources("*.txt"),
+                "CMakeLists.txt": "mycmake"})
+        t.run("create .")
+        t.run("upload * -r=default -c")
+
+        c = TestClient(servers=t.servers)
+        c.run("workspace open pkg/0.1")
+        assert "name = 'pkg'" in c.load("pkg/conanfile.py")
+        assert "mycmake" in c.load("pkg/CMakeLists.txt")
+
+    def test_workspace_git_scm(self):
+        folder = temp_folder()
+        conanfile = textwrap.dedent("""
+            from conan import ConanFile
+            from conan.tools.scm import Git
+
+            class Pkg(ConanFile):
+                name = "pkg"
+                version = "0.1"
+                def export(self):
+                    git = Git(self)
+                    git.coordinates_to_conandata()
+            """)
+        url, commit = create_local_git_repo(files={"conanfile.py": conanfile}, folder=folder,
+                                            branch="mybranch")
+        t1 = TestClient(default_server_user=True, light=True)
+        t1.run_command('git clone "file://{}" .'.format(url))
+        t1.run("create .")
+        t1.run("upload * -r=default -c")
+
+        c = TestClient(servers=t1.servers, light=True)
+        c.run("workspace open pkg/0.1")
+        assert c.load("pkg/conanfile.py") == conanfile
+
+        c2 = TestClient(servers=t1.servers, light=True)
+        c2.save({"conanws.py": ""})
+        c2.run(f"workspace add --ref=pkg/0.1")
+        assert 'name = "pkg"' in c2.load("pkg/conanfile.py")
+        c2.run("workspace info")
+        assert "pkg/0.1" in c2.out
+
+    def test_workspace_build_editables(self):
+        c = TestClient(light=True)
+        c.save({"conanws.yml": ""})
+
+        c.save({"pkga/conanfile.py": GenConanfile("pkga", "0.1").with_build_msg("BUILD PKGA!"),
+                "pkgb/conanfile.py": GenConanfile("pkgb", "0.1").with_build_msg("BUILD PKGB!")
+                                                                .with_requires("pkga/0.1")})
+        c.run("workspace add pkga")
+        c.run("workspace add pkgb")
+
+        c.run("install --requires=pkgb/0.1 --build=editable")
+        c.assert_listed_binary({"pkga/0.1": ("da39a3ee5e6b4b0d3255bfef95601890afd80709",
+                                             "EditableBuild"),
+                                "pkgb/0.1": ("47a5f20ec8fb480e1c5794462089b01a3548fdc5",
+                                             "EditableBuild")})
+        assert "pkga/0.1: WARN: BUILD PKGA!" in c.out
+        assert "pkgb/0.1: WARN: BUILD PKGB!" in c.out
diff --git a/test/unittests/model/build_info/test_deduce_locations.py b/test/unittests/model/build_info/test_deduce_locations.py
index 610cd2a87..5aeeff77f 100644
--- a/test/unittests/model/build_info/test_deduce_locations.py
+++ b/test/unittests/model/build_info/test_deduce_locations.py
@@ -1,28 +1,52 @@
 import os
+import platform
+from unittest.mock import MagicMock
 
+import pytest
+
+from conan.errors import ConanException
 from conan.test.utils.mocks import ConanFileMock
 from conan.test.utils.test_files import temp_folder
+from conan.tools.files import save
 from conans.model.build_info import CppInfo
+from conans.model.pkg_type import PackageType
+from conans.model.recipe_ref import RecipeReference
 from conans.util.files import save
 
 
-def test_deduce_locations():
+@pytest.fixture
+def conanfile():
+    c = ConanFileMock()
+    c._conan_node = MagicMock(ref=RecipeReference(""))
+    return c
+
+
+@pytest.mark.parametrize("lib_name, libs", [
+    ("myliblin.a", ["myliblin"]),
+    ("libmyliblin.a", ["myliblin"]),
+    ("mylibmac.a", ["mylibmac"]),
+    ("mylibwin.lib", ["mylibwin"]),
+    ("libmylibwin.lib", ["libmylibwin"]),
+    ("mylibwin2.if.lib", ["mylibwin2.if.lib"]),
+    ("mylibwin2.if.lib", ["mylibwin2"])
+])
+def test_simple_deduce_locations_static(lib_name, libs, conanfile):
     folder = temp_folder()
-    location = os.path.join(folder, "libdir", "mylib.lib")
+    location = os.path.join(folder, "libdir", lib_name)
     save(location, "")
 
     cppinfo = CppInfo()
     cppinfo.libdirs = ["libdir"]
-    cppinfo.libs = ["mylib"]
+    cppinfo.libs = libs
     cppinfo.set_relative_base_folder(folder)
 
-    result = cppinfo.deduce_full_cpp_info(ConanFileMock())
+    result = cppinfo.deduce_full_cpp_info(conanfile)
     assert result.location == location.replace("\\", "/")
     assert result.link_location is None
     assert result.type == "static-library"
 
 
-def test_deduce_shared_link_locations():
+def test_deduce_shared_link_locations(conanfile):
     folder = temp_folder()
     imp_location = os.path.join(folder, "libdir", "mylib.lib")
     save(imp_location, "")
@@ -35,7 +59,165 @@ def test_deduce_shared_link_locations():
     cppinfo.libs = ["mylib"]
     cppinfo.set_relative_base_folder(folder)
 
-    result = cppinfo.deduce_full_cpp_info(ConanFileMock())
+    result = cppinfo.deduce_full_cpp_info(conanfile)
+    assert result.location == location.replace("\\", "/")
+    assert result.link_location == imp_location.replace("\\", "/")
+    assert result.type == "shared-library"
+
+
+@pytest.mark.parametrize("lib_name, libs", [
+    ("liblog4cxx.so.15.2.0", ["log4cxx"]),
+    ("libapr-1.0.dylib", ["apr-1"]),
+    ("libapr-1.so.0.7.4", ["apr-1"])
+])
+def test_complex_deduce_locations_shared(lib_name, libs, conanfile):
+    """
+    Tests real examples of shared library names in Linux/MacOS,
+    e.g., log4cxx, apr-1, etc.
+
+    Related issue: https://github.com/conan-io/conan/issues/16990
+    """
+    folder = temp_folder()
+    location = os.path.join(folder, "libdir", lib_name)
+    save(location, "")
+
+    cppinfo = CppInfo()
+    cppinfo.libdirs = ["libdir"]
+    cppinfo.libs = libs
+    cppinfo.set_relative_base_folder(folder)
+
+    result = cppinfo.deduce_full_cpp_info(conanfile)
+    assert result.location == location.replace("\\", "/")
+    assert result.link_location is None
+    assert result.type == "shared-library"
+
+
+@pytest.mark.parametrize("lib_name, dll_name, libs, pkg_name", [
+    ("libcurl_imp.lib", "libcurl.dll", ["libcurl_imp"], "libcurl"),
+    ("libcrypto.lib", "libcrypto-3-x64.dll", ["libcrypto"], "crypto"),
+    ("libssl.lib", "libssl-3-x64.dll", ["libssl"], "ssl"),
+    ("zdll.lib", "zlib1.dll", ["zdll"], "zlib")
+])
+def test_windows_shared_link_locations(lib_name, dll_name, libs, pkg_name, conanfile):
+    """
+    Tests real examples of shared library names in Windows,
+    e.g., openssl, zlib, libcurlb, etc.
+    """
+    folder = temp_folder()
+    imp_location = os.path.join(folder, "libdir", lib_name)
+    save(imp_location, "")
+    location = os.path.join(folder, "bindir", dll_name)
+    save(location, "")
+
+    cppinfo = CppInfo()
+    cppinfo.libdirs = ["libdir"]
+    cppinfo.bindirs = ["bindir"]
+    cppinfo.libs = libs
+    cppinfo.set_relative_base_folder(folder)
+
+    conanfile._conan_node.ref = RecipeReference(name=pkg_name)
+    result = cppinfo.deduce_full_cpp_info(conanfile)
     assert result.location == location.replace("\\", "/")
     assert result.link_location == imp_location.replace("\\", "/")
     assert result.type == "shared-library"
+
+
+@pytest.mark.parametrize("lib_info", [
+    {"charset": ["charset.lib", "charset-1.dll"],
+     "iconv": ["iconv.lib", "iconv-2.dll"]},
+    {"charset": ["libcharset.so.1.0.0"],
+     "iconv": ["libiconv.so.2.6.1"]},
+])
+def test_windows_several_shared_link_locations(lib_info, conanfile):
+    """
+    Tests a real model as LIBICONV with several libs defined in the root component
+    """
+    folder = temp_folder()
+    locations = {}
+    is_windows = False
+    for lib_name, lib_files in lib_info.items():
+        imp_location = os.path.join(folder, "libdir", lib_files[0])
+        save(imp_location, "")
+        if len(lib_files) > 1:
+            is_windows = True
+            location = os.path.join(folder, "bindir", lib_files[1])
+            save(location, "")
+        else:
+            location = imp_location  # Linux
+        locations[lib_name] = (location, imp_location)
+
+    cppinfo = CppInfo()
+    cppinfo.libdirs = ["libdir"]
+    cppinfo.bindirs = ["bindir"]
+    cppinfo.libs = list(lib_info.keys())
+    cppinfo.set_relative_base_folder(folder)
+
+    result = cppinfo.deduce_full_cpp_info(conanfile)
+    for lib_name in lib_info:
+        assert result.components[f"_{lib_name}"].location == locations[lib_name][0].replace("\\", "/")
+        if is_windows:
+            assert result.components[f"_{lib_name}"].link_location == locations[lib_name][1].replace("\\", "/")
+        assert result.components[f"_{lib_name}"].type == "shared-library"
+
+
+@pytest.mark.skipif(platform.system() == "Windows", reason="Can't apply symlink on Windows")
+def test_shared_link_locations_symlinks(conanfile):
+    """
+    Tests auto deduce location is able to find the real path of
+    any symlink created in the libs folder
+    """
+    folder = temp_folder()
+    # forcing a folder that it's not going to be analysed by the deduce_location() function
+    real_location = os.path.join(folder, "other", "mylib.so")
+    save(real_location, "")
+    # Symlinks
+    os.makedirs(os.path.join(folder, "libdir"))
+    sym_1 = os.path.join(folder, "libdir", "mylib.1.0.0.so")
+    sym_2 = os.path.join(folder, "libdir", "mylib.2.0.0.so")
+    os.symlink(real_location, sym_1)
+    os.symlink(sym_1, sym_2)
+
+    cppinfo = CppInfo()
+    cppinfo.libdirs = ["libdir"]
+    cppinfo.libs = ["mylib"]
+    cppinfo.set_relative_base_folder(folder)
+
+    result = cppinfo.deduce_full_cpp_info(conanfile)
+    assert result.location == real_location
+    assert result.type == "shared-library"
+
+
+@pytest.mark.parametrize("static", [True, False])
+def test_error_if_shared_and_static_found(static, conanfile):
+    folder = temp_folder()
+    save(os.path.join(folder, "libdir", "libmylib.a"), "")
+    save(os.path.join(folder, "libdir", "libmylib.so"), "")
+
+    cppinfo = CppInfo()
+    cppinfo.libdirs = ["libdir"]
+    cppinfo.libs = ["mylib"]
+    cppinfo.set_relative_base_folder(folder)
+    folder = folder.replace("\\", "/")
+    if static:
+        conanfile.package_type = PackageType.STATIC
+    result = cppinfo.deduce_full_cpp_info(conanfile)
+    ext = "a" if static else "so"
+    assert result.location == f"{folder}/libdir/libmylib.{ext}"
+    assert result.type == (PackageType.STATIC if static else PackageType.SHARED)
+
+
+def test_warning_windows_if_more_than_one_dll(conanfile):
+    folder = temp_folder()
+    save(os.path.join(folder, "libdir", "mylib.a"), "")
+    save(os.path.join(folder, "bindir", "libx.dll"), "")
+    save(os.path.join(folder, "bindir", "liby.dll"), "")
+
+    cppinfo = CppInfo()
+    cppinfo.libdirs = ["libdir"]
+    cppinfo.bindirs = ["bindir"]
+    cppinfo.libs = ["mylib"]
+    cppinfo.set_relative_base_folder(folder)
+    folder = folder.replace("\\", "/")
+    result = cppinfo.deduce_full_cpp_info(conanfile)
+    assert result.location == f"{folder}/bindir/libx.dll"
+    assert result.type == "shared-library"
diff --git a/test/unittests/tools/cmake/test_cmake_cmd_line_args.py b/test/unittests/tools/cmake/test_cmake_cmd_line_args.py
index 7aa8a2f20..b8848cda9 100644
--- a/test/unittests/tools/cmake/test_cmake_cmd_line_args.py
+++ b/test/unittests/tools/cmake/test_cmake_cmd_line_args.py
@@ -44,3 +44,13 @@ def test_visual_studio(conanfile):
 
     args = _cmake_cmd_line_args(conanfile, 'Ninja')
     assert args == ['-j10']
+
+
+def test_maxcpucount_zero():
+    c = ConfDefinition()
+    c.loads("tools.microsoft.msbuild:max_cpu_count=0")
+
+    conanfile = ConanFileMock()
+    conanfile.conf = c.get_conanfile_conf(None)
+    args = _cmake_cmd_line_args(conanfile, 'Visual Studio 16 2019')
+    assert ["/m"] == args
diff --git a/test/unittests/tools/google/test_bazel.py b/test/unittests/tools/google/test_bazel.py
index 3aa21d364..b6250f6ee 100644
--- a/test/unittests/tools/google/test_bazel.py
+++ b/test/unittests/tools/google/test_bazel.py
@@ -8,7 +8,7 @@ from conan.test.utils.mocks import ConanFileMock
 from conan.test.utils.test_files import temp_folder
 from conan.tools.files import save
 from conan.tools.google import Bazel
-from conan.tools.google.bazeldeps import _relativize_path, _get_libs
+from conan.tools.google.bazeldeps import _relativize_path
 
 
 @pytest.fixture(scope="module")
@@ -77,45 +77,3 @@ def test_bazel_command_with_config_values():
 ])
 def test_bazeldeps_relativize_path(path, pattern, expected):
     assert _relativize_path(path, pattern) == expected
-
-
-@pytest.mark.parametrize("libs, is_shared, expected", [
-    # expected == (lib_name, is_shared, library_path, interface_library_path)
-    (["mylibwin"], False, [('mylibwin', False, '{base_folder}/lib/mylibwin.lib', None)]),
-    # Win + shared
-    (["mylibwin"], True, [('mylibwin', True, '{base_folder}/bin/mylibwin.dll', '{base_folder}/lib/mylibwin.lib')]),
-    # Win + shared (interface with another ext)
-    (["mylibwin2"], True, [('mylibwin2', True, '{base_folder}/bin/mylibwin2.dll', '{base_folder}/lib/mylibwin2.if.lib')]),
-    # Mac + shared
-    (["mylibmac"], True, [('mylibmac', True, '{base_folder}/bin/mylibmac.dylib', None)]),
-    # Mac + static
-    (["mylibmac"], False, [('mylibmac', False, '{base_folder}/lib/mylibmac.a', None)]),
-    # mylib + shared (saved as libmylib.so) -> removing the leading "lib" if it matches
-    (["mylib"], True, [('mylib', True, '{base_folder}/lib/libmylib.so', None)]),
-    # mylib + static (saved in a subfolder subfolder/libmylib.a) -> non-recursive at this moment
-    (["mylib"], False, []),
-    # no lib matching
-    (["noexist"], False, []),
-    # no lib matching + Win + static
-    (["noexist", "mylibwin"], False, [('mylibwin', False, '{base_folder}/lib/mylibwin.lib', None)]),
-    # protobuf (Issue related https://github.com/conan-io/conan/issues/15390)
-    (["protoc"], True, []),
-    # non-conventional library name (Issue related https://github.com/conan-io/conan/pull/11343)
-    (["libmylib.so"], True, [('libmylib.so', True, '{base_folder}/lib/libmylib.so', None)]),
-])
-def test_bazeldeps_get_libs(cpp_info, libs, is_shared, expected):
-    cpp_info.libs = libs
-    ret = []
-    for (lib, is_shared, lib_path, interface_lib_path) in expected:
-        if lib_path:
-            lib_path = lib_path.format(base_folder=cpp_info._base_folder)
-        if interface_lib_path:
-            interface_lib_path = interface_lib_path.format(base_folder=cpp_info._base_folder)
-        ret.append((lib, is_shared, lib_path, interface_lib_path))
-    dep = MagicMock()
-    dep.options.get_safe.return_value = is_shared
-    dep.ref.name = "my_pkg"
-    found_libs = _get_libs(dep, cpp_info)
-    found_libs.sort()
-    ret.sort()
-    assert found_libs == ret
diff --git a/test/unittests/tools/microsoft/test_msbuild.py b/test/unittests/tools/microsoft/test_msbuild.py
index 3fc984531..3039fbefd 100644
--- a/test/unittests/tools/microsoft/test_msbuild.py
+++ b/test/unittests/tools/microsoft/test_msbuild.py
@@ -1,5 +1,4 @@
 import os
-import textwrap
 
 import pytest
 
@@ -31,14 +30,9 @@ def test_msbuild_targets():
 
 def test_msbuild_cpu_count():
     c = ConfDefinition()
-    c.loads(textwrap.dedent("""\
-        tools.microsoft.msbuild:max_cpu_count=23
-    """))
+    c.loads("tools.microsoft.msbuild:max_cpu_count=23")
 
     settings = MockSettings({"build_type": "Release",
-                             "compiler": "gcc",
-                             "compiler.version": "7",
-                             "os": "Linux",
                              "arch": "x86_64"})
     conanfile = ConanFileMock()
     conanfile.settings = settings
@@ -46,8 +40,12 @@ def test_msbuild_cpu_count():
 
     msbuild = MSBuild(conanfile)
     cmd = msbuild.command('project.sln')
+    assert 'msbuild "project.sln" /p:Configuration="Release" /p:Platform=x64 /m:23' == cmd
 
-    assert '/m:23' in cmd
+    c.loads("tools.microsoft.msbuild:max_cpu_count=0")
+    conanfile.conf = c.get_conanfile_conf(None)
+    cmd = msbuild.command('project.sln')
+    assert 'msbuild "project.sln" /p:Configuration="Release" /p:Platform=x64 /m' == cmd
 
 
 def test_msbuild_toolset():
@@ -81,10 +79,10 @@ def test_msbuild_toolset_for_intel_cc(mode, expected_toolset):
     conanfile = ConanFile()
     conanfile.settings = "os", "compiler", "build_type", "arch"
     conanfile.settings = Settings({"build_type": ["Release"],
-                         "compiler": {"intel-cc": {"version": ["2021.3"], "mode": [mode]},
-                                      "msvc": {"version": ["193"], "cppstd": ["20"]}},
-                         "os": ["Windows"],
-                         "arch": ["x86_64"]})
+                                   "compiler": {"intel-cc": {"version": ["2021.3"], "mode": [mode]},
+                                                "msvc": {"version": ["193"], "cppstd": ["20"]}},
+                                   "os": ["Windows"],
+                                   "arch": ["x86_64"]})
     conanfile.settings.build_type = "Release"
     conanfile.settings.compiler = "intel-cc"
     conanfile.settings.compiler.version = "2021.3"