Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/workspace base #17272

Merged
merged 43 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
3a07a4e
refactor: move editables to local api
memsharded Dec 31, 2023
1dd4458
workspaces back
memsharded Jan 1, 2024
1e1b033
wip
memsharded Jan 2, 2024
f610925
Git.to_conandata and from_conandata
memsharded Jan 3, 2024
da86f35
Raise error if `check_type=int` and conf value is set to `bool` (#15378)
czoido Jan 3, 2024
d586b6b
auto -FS in AutotoolsToolchain (#15375)
memsharded Jan 3, 2024
8821d58
fix winsdk_version bug (#15373)
memsharded Jan 4, 2024
990b254
allow to copy & paste from compact format into conan-lock-add (#15262)
memsharded Jan 4, 2024
cda0e8f
Merge branch 'develop2' into feature/git_to_conandata
memsharded Jan 8, 2024
e99834e
Merge branch 'feature/git_to_conandata' into feature/workspace
memsharded Jan 8, 2024
06b1b2e
wip
memsharded Jan 9, 2024
5e49fee
wip
memsharded Jan 15, 2024
dd78307
Merge branch 'develop2' into feature/workspace
memsharded Jan 15, 2024
3e64b89
open + add + remove proposal
memsharded Jan 15, 2024
c2997e6
Merge branch 'develop2' into feature/workspace
memsharded Jan 17, 2024
446c556
wip
memsharded Jan 18, 2024
300248d
Merge branch 'develop2' into feature/workspace
memsharded Jan 19, 2024
cfb33d1
wip
memsharded Jan 19, 2024
c53d5b9
Merge branch 'develop2' into feature/workspace
memsharded Jan 19, 2024
b34d7a6
wip
memsharded Jan 19, 2024
8281e2a
wip
memsharded Jul 7, 2024
4aeafea
wip
memsharded Jul 7, 2024
3167191
wip
memsharded Jul 7, 2024
ed9d748
Merge branch 'develop2' into feature/workspace
memsharded Sep 19, 2024
d4cd077
wip
memsharded Sep 19, 2024
f1beb50
wip
memsharded Sep 20, 2024
8967e99
Merge branch 'develop2' into feature/workspace
memsharded Nov 4, 2024
9f031c1
initial minimal workspace
memsharded Nov 4, 2024
ff40195
merged develop2
memsharded Nov 6, 2024
2dc4379
wip
memsharded Nov 6, 2024
800c621
Merge branch 'develop2' into feature/workspace_base
memsharded Nov 7, 2024
992f6bf
new test
memsharded Nov 7, 2024
6cac832
minor change to make test fail
czoido Nov 8, 2024
fe7ac0e
making workspace decoupled from editables
memsharded Nov 8, 2024
4a472b0
Merge branch 'develop2' into feature/workspace_base
memsharded Nov 11, 2024
92560eb
fix docstring
memsharded Nov 11, 2024
83b1912
Merge branch 'develop2' into feature/workspace_base
memsharded Nov 12, 2024
a377ab7
new test for dynamic workspace
memsharded Nov 12, 2024
6c023e3
UserWorkpaceAPI
memsharded Nov 12, 2024
573d5aa
Merge branch 'develop2' into feature/workspace_base
memsharded Nov 21, 2024
9546c8f
fix test
memsharded Nov 21, 2024
0a642b9
review
memsharded Nov 21, 2024
9010da2
Merge branch 'develop2' into feature/workspace_base
memsharded Nov 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion conan/api/conan_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
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
Expand Down Expand Up @@ -32,7 +33,8 @@ def __init__(self, cache_folder=None):
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
Expand Down
13 changes: 9 additions & 4 deletions conan/api/subapi/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -55,17 +58,20 @@ def editable_add(self, path, name=None, version=None, user=None, channel=None, c
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
Expand Down Expand Up @@ -116,4 +122,3 @@ def inspect(self, conanfile_path, remotes, lockfile, name=None, version=None, us
conanfile = app.loader.load_named(conanfile_path, name=name, version=version, user=user,
channel=channel, remotes=remotes, graph_lock=lockfile)
return conanfile

82 changes: 82 additions & 0 deletions conan/api/subapi/workspace.py
Original file line number Diff line number Diff line change
@@ -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()
108 changes: 108 additions & 0 deletions conan/cli/commands/workspace.py
Original file line number Diff line number Diff line change
@@ -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)
"""
Loading