From 0eea45f85623de1fce5e8b48513488c0937eedf8 Mon Sep 17 00:00:00 2001 From: memsharded Date: Tue, 4 Mar 2025 01:01:38 +0100 Subject: [PATCH 1/2] workspace install partial --- conan/api/subapi/workspace.py | 11 ++++++ conan/cli/commands/workspace.py | 15 +++++--- test/integration/workspace/test_workspace.py | 40 ++++++++++++++++++++ 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/conan/api/subapi/workspace.py b/conan/api/subapi/workspace.py index 8eb07749697..15a49a1779c 100644 --- a/conan/api/subapi/workspace.py +++ b/conan/api/subapi/workspace.py @@ -122,6 +122,17 @@ def editable_packages(self): v["output_folder"])) return editables + def select_editables(self, paths, refs): + filtered_refs = [RecipeReference.loads(r) for r in refs or []] + filtered_refs.extend(self.editable_from_path(p) for p in paths or []) + editables = self.editable_packages + requires = [ref for ref in editables] + if filtered_refs: + ConanOutput().info(f"Filtering and installing only selected editable packages") + requires = [ref for ref in requires if ref in filtered_refs] + ConanOutput().info(f"Filtered references: {requires}") + return requires + @property def products(self): self._check_ws() diff --git a/conan/cli/commands/workspace.py b/conan/cli/commands/workspace.py index ed49e7820ee..842e5ec3aba 100644 --- a/conan/cli/commands/workspace.py +++ b/conan/cli/commands/workspace.py @@ -50,8 +50,7 @@ def workspace_add(conan_api: ConanAPI, parser, subparser, *args): 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("--ref", 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() @@ -78,9 +77,10 @@ def workspace_add(conan_api: ConanAPI, parser, subparser, *args): @conan_subcommand() def workspace_remove(conan_api: ConanAPI, parser, subparser, *args): """ - Remove packages to current workspace + Remove packages from the current workspace """ - subparser.add_argument('path', help='Path to the package folder in the user 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}") @@ -170,6 +170,10 @@ def workspace_install(conan_api: ConanAPI, parser, subparser, *args): Install the workspace as a monolith, installing only external dependencies to the workspace, generating a single result (generators, etc) for the whole workspace. """ + subparser.add_argument("path", nargs="*", + help="Install only these editable packages, not all") + subparser.add_argument("--ref", action="append", + help="Install only these editable packages, not all") subparser.add_argument("-g", "--generator", action="append", help='Generators to use') subparser.add_argument("-of", "--output-folder", help='The root output folder for generated and build files') @@ -191,8 +195,7 @@ def workspace_install(conan_api: ConanAPI, parser, subparser, *args): conan_api.workspace.info() # FIXME: Just to force error if WS not enabled # Build a dependency graph with all editables as requirements - editables = conan_api.workspace.editable_packages - requires = [ref for ref in editables] + requires = conan_api.workspace.select_editables(args.path, args.ref) if not requires: raise ConanException("This workspace cannot be installed, it doesn't have any editable") deps_graph = conan_api.graph.load_graph_requires(requires, [], diff --git a/test/integration/workspace/test_workspace.py b/test/integration/workspace/test_workspace.py index 2fabaf9a355..e3d8439aeaa 100644 --- a/test/integration/workspace/test_workspace.py +++ b/test/integration/workspace/test_workspace.py @@ -1,5 +1,6 @@ import json import os +import shutil import textwrap import pytest @@ -254,6 +255,19 @@ def test_remove_product(self): c.run("workspace info") assert "mydeppkg" not in c.out + def test_remove_removed_folder(self): + c = TestClient(light=True) + c.save({"conanws.yml": "", + "mydeppkg/conanfile.py": GenConanfile("mydeppkg", "0.1")}) + c.run("workspace add mydeppkg") + # If we now remove the folder + shutil.rmtree(os.path.join(c.current_folder, "mydeppkg")) + # It can still be removed by path, even if the path doesn't exist + c.run("workspace remove mydeppkg") + assert "Removed from workspace: mydeppkg/0.1" in c.out + c.run("workspace info") + assert "mydeppkg" not in c.out + def test_custom_add_remove(self): c = TestClient(light=True) @@ -524,6 +538,32 @@ def root_conanfile(self): c.run("workspace install", assert_error=True) assert "ERROR: Conanfile in conanws.py shouldn't have 'requires'" in c.out + def test_install_partial(self): + # If we want to install only some part of the workspace + c = TestClient() + c.save({"dep/conanfile.py": GenConanfile()}) + c.run("create dep --name=dep1 --version=0.1") + c.run("create dep --name=dep2 --version=0.1") + c.save({"conanws.yml": "", + "liba/conanfile.py": GenConanfile("liba", "0.1").with_requires("dep1/0.1"), + "libb/conanfile.py": GenConanfile("libb", "0.1").with_requires("liba/0.1"), + "libc/conanfile.py": GenConanfile("libc", "0.1").with_requires("libb/0.1", + "dep2/0.1")}, + clean_first=True) + c.run("workspace add liba") + c.run("workspace add libb") + c.run("workspace add libc") + for arg in ("--ref=libb/0.1", "--ref=libb/0.1 --ref=liba/0.1", + "libb", "libb liba"): + c.run(f"workspace install {arg} -g CMakeDeps -of=build") + print(c.out) + assert "dep1/0.1" in c.out + assert "dep2/0.1" not in c.out + assert "libc/0.1" not in c.out + files = os.listdir(os.path.join(c.current_folder, "build")) + assert "dep1-config.cmake" in files + assert "dep2-config.cmake" not in files + def test_workspace_with_local_recipes_index(): c3i_folder = temp_folder() From 5d33f3d6a9affa079ec906e1f406a2a75c7b8406 Mon Sep 17 00:00:00 2001 From: memsharded Date: Tue, 4 Mar 2025 01:08:27 +0100 Subject: [PATCH 2/2] remove --ref arg --- conan/api/subapi/workspace.py | 5 ++--- conan/cli/commands/workspace.py | 4 +--- test/integration/workspace/test_workspace.py | 4 +--- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/conan/api/subapi/workspace.py b/conan/api/subapi/workspace.py index 15a49a1779c..09dd7cbe29a 100644 --- a/conan/api/subapi/workspace.py +++ b/conan/api/subapi/workspace.py @@ -122,9 +122,8 @@ def editable_packages(self): v["output_folder"])) return editables - def select_editables(self, paths, refs): - filtered_refs = [RecipeReference.loads(r) for r in refs or []] - filtered_refs.extend(self.editable_from_path(p) for p in paths or []) + def select_editables(self, paths): + filtered_refs = [self.editable_from_path(p) for p in paths or []] editables = self.editable_packages requires = [ref for ref in editables] if filtered_refs: diff --git a/conan/cli/commands/workspace.py b/conan/cli/commands/workspace.py index 842e5ec3aba..0dfbf2a2792 100644 --- a/conan/cli/commands/workspace.py +++ b/conan/cli/commands/workspace.py @@ -172,8 +172,6 @@ def workspace_install(conan_api: ConanAPI, parser, subparser, *args): """ subparser.add_argument("path", nargs="*", help="Install only these editable packages, not all") - subparser.add_argument("--ref", action="append", - help="Install only these editable packages, not all") subparser.add_argument("-g", "--generator", action="append", help='Generators to use') subparser.add_argument("-of", "--output-folder", help='The root output folder for generated and build files') @@ -195,7 +193,7 @@ def workspace_install(conan_api: ConanAPI, parser, subparser, *args): conan_api.workspace.info() # FIXME: Just to force error if WS not enabled # Build a dependency graph with all editables as requirements - requires = conan_api.workspace.select_editables(args.path, args.ref) + requires = conan_api.workspace.select_editables(args.path) if not requires: raise ConanException("This workspace cannot be installed, it doesn't have any editable") deps_graph = conan_api.graph.load_graph_requires(requires, [], diff --git a/test/integration/workspace/test_workspace.py b/test/integration/workspace/test_workspace.py index e3d8439aeaa..982f99d1a6b 100644 --- a/test/integration/workspace/test_workspace.py +++ b/test/integration/workspace/test_workspace.py @@ -553,10 +553,8 @@ def test_install_partial(self): c.run("workspace add liba") c.run("workspace add libb") c.run("workspace add libc") - for arg in ("--ref=libb/0.1", "--ref=libb/0.1 --ref=liba/0.1", - "libb", "libb liba"): + for arg in ("libb", "libb liba"): c.run(f"workspace install {arg} -g CMakeDeps -of=build") - print(c.out) assert "dep1/0.1" in c.out assert "dep2/0.1" not in c.out assert "libc/0.1" not in c.out