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

[develop2] moving global graph_compute to GraphAPI #12666

Merged
merged 7 commits into from
Dec 12, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
92 changes: 86 additions & 6 deletions conan/api/subapi/graph.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import os

from conan.api.output import ConanOutput
from conan.api.subapi import api_method
from conan.api.conan_app import ConanApp
from conan.cli.printers.graph import print_graph_basic
from conans.client.graph.graph import Node, RECIPE_CONSUMER, CONTEXT_HOST, RECIPE_VIRTUAL
from conans.client.graph.graph_binaries import GraphBinariesAnalyzer
from conans.client.graph.graph_builder import DepsGraphBuilder
Expand All @@ -18,10 +20,9 @@ class GraphAPI:
def __init__(self, conan_api):
self.conan_api = conan_api

@api_method
def load_root_consumer_conanfile(self, path, profile_host, profile_build,
name=None, version=None, user=None, channel=None,
update=None, remotes=None, lockfile=None):
def _load_root_consumer_conanfile(self, path, profile_host, profile_build,
name=None, version=None, user=None, channel=None,
update=None, remotes=None, lockfile=None):
app = ConanApp(self.conan_api.cache_folder)

if path.endswith(".py"):
Expand Down Expand Up @@ -75,7 +76,8 @@ def load_root_test_conanfile(self, path, tested_reference, profile_host, profile
conanfile = loader.load_consumer(path, user=tested_reference.user,
channel=tested_reference.channel,
graph_lock=lockfile, remotes=remotes,
tested_python_requires=tested_python_requires)
tested_python_requires=tested_python_requires,
update=update)
initialize_conanfile_profile(conanfile, profile_build, profile_host, CONTEXT_HOST, False)
conanfile.display_name = "%s (test package)" % str(tested_reference)
conanfile.output.scope = conanfile.display_name
Expand All @@ -87,7 +89,7 @@ def load_root_test_conanfile(self, path, tested_reference, profile_host, profile
return root_node

@api_method
def load_root_virtual_conanfile(self, profile_host, requires=None, tool_requires=None):
def _load_root_virtual_conanfile(self, profile_host, requires=None, tool_requires=None):
Comment on lines 91 to +92
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be marked as an API method and have an underscore? I thought underscore meant private function? 🤔

Copy link
Member Author

@memsharded memsharded Dec 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not, the "virtual" conanfile seems a bit of implementation detail, I don't see a clear use case of users having to do that, now that we exposed a load_graph_consumer(path, ...) and load_graph_requires(requires, tool_requires...) methods. We don't need to do it now, lets keep the API minimal to the construction of our own built-in commands, and open it as necessary when requested by users. So this might be good as private at the moment, it will not be part of the public api.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree it should be private, thats why I am confused why it's @api_method :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see, you are right, that api_method should be removed.

if not requires and not tool_requires:
raise ConanException("Provide requires or tool_requires")
app = ConanApp(self.conan_api.cache_folder)
Expand All @@ -96,6 +98,84 @@ def load_root_virtual_conanfile(self, profile_host, requires=None, tool_requires
root_node = Node(ref=None, conanfile=conanfile, context=CONTEXT_HOST, recipe=RECIPE_VIRTUAL)
return root_node

@staticmethod
def _scope_options(profile, requires, tool_requires):
"""
Command line helper to scope options when ``command -o myoption=myvalue`` is used,
that needs to be converted to "-o pkg:myoption=myvalue". The "pkg" value will be
computed from the given requires/tool_requires
"""
# FIXME: This helper function here is not great, find a better place
if requires and len(requires) == 1 and not tool_requires:
profile.options.scope(requires[0])
if tool_requires and len(tool_requires) == 1 and not requires:
profile.options.scope(tool_requires[0])

@api_method
def load_graph_requires(self, requires, tool_requires, profile_host, profile_build,
lockfile, remotes, update, check_updates=False, allow_error=False):
requires = [RecipeReference.loads(r) if isinstance(r, str) else r for r in requires] \
if requires else None
tool_requires = [RecipeReference.loads(r) if isinstance(r, str) else r
for r in tool_requires] if tool_requires else None

out = ConanOutput()
out.title("Input profiles")
out.info("Profile host:")
out.info(profile_host.dumps())
out.info("Profile build:")
out.info(profile_build.dumps())

self._scope_options(profile_host, requires=requires, tool_requires=tool_requires)
root_node = self._load_root_virtual_conanfile(requires=requires, tool_requires=tool_requires,
profile_host=profile_host)

out.title("Computing dependency graph")
# check_updates = args.check_updates if "check_updates" in args else False
deps_graph = self.load_graph(root_node, profile_host=profile_host,
profile_build=profile_build,
lockfile=lockfile,
remotes=remotes,
update=update,
check_update=check_updates)
print_graph_basic(deps_graph)
out.title("Computing necessary packages")
if deps_graph.error:
if allow_error:
return deps_graph
raise deps_graph.error

return deps_graph

@api_method
def load_graph_consumer(self, path, name, version, user, channel,
profile_host, profile_build, lockfile, remotes, update,
allow_error=False, check_updates=False):
out = ConanOutput()
out.title("Input profiles")
out.info("Profile host:")
out.info(profile_host.dumps())
out.info("Profile build:")
out.info(profile_build.dumps())

root_node = self._load_root_consumer_conanfile(path, profile_host, profile_build,
name=name, version=version, user=user,
channel=channel, lockfile=lockfile,
remotes=remotes, update=update)

out.title("Computing dependency graph")
deps_graph = self.load_graph(root_node, profile_host=profile_host,
profile_build=profile_build, lockfile=lockfile,
remotes=remotes, update=update, check_update=check_updates)
print_graph_basic(deps_graph)
out.title("Computing necessary packages")
if deps_graph.error:
if allow_error:
return deps_graph
raise deps_graph.error

return deps_graph

@api_method
def load_graph(self, root_node, profile_host, profile_build, lockfile=None, remotes=None,
update=False, check_update=False):
Expand Down
17 changes: 15 additions & 2 deletions conan/cli/commands/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
from conan.api.output import ConanOutput
from conan.cli.command import conan_command
from conan.cli.commands import make_abs_path
from conan.cli.commands.install import graph_compute
from conan.cli.args import add_lockfile_args, _add_common_install_arguments, add_reference_args, \
_help_build_policies
from conan.api.conan_app import ConanApp
from conan.cli.printers.graph import print_graph_packages
from conans.client.conanfile.build import run_build_method


Expand All @@ -31,7 +31,20 @@ def build(conan_api, parser, *args):
folder = os.path.dirname(path)
remotes = conan_api.remotes.list(args.remote)

deps_graph, lockfile = graph_compute(args, conan_api, partial=args.lockfile_partial)
lockfile = conan_api.lockfile.get_lockfile(lockfile=args.lockfile,
conanfile_path=path,
cwd=cwd,
partial=args.lockfile_partial)
profile_host, profile_build = conan_api.profiles.get_profiles_from_args(args)

deps_graph = conan_api.graph.load_graph_consumer(path, args.name, args.version,
args.user, args.channel,
profile_host, profile_build, lockfile, remotes,
args.update)

conan_api.graph.analyze_binaries(deps_graph, args.build, remotes=remotes, update=args.update,
lockfile=lockfile)
print_graph_packages(deps_graph)

out = ConanOutput()
out.title("Installing packages")
Expand Down
14 changes: 5 additions & 9 deletions conan/cli/commands/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from conan.api.output import ConanOutput, cli_out_write
from conan.cli.command import conan_command, OnceArgument
from conan.cli.commands.export import common_args_export
from conan.cli.common import scope_options
from conan.cli.args import add_lockfile_args, _add_common_install_arguments, _help_build_policies
from conan.api.conan_app import ConanApp
from conan.cli.printers.graph import print_graph_basic, print_graph_packages
Expand Down Expand Up @@ -70,16 +69,13 @@ def create(conan_api, parser, *args):
# TODO: This section might be overlapping with ``graph_compute()``
requires = [ref] if not args.build_require else None
tool_requires = [ref] if args.build_require else None
scope_options(profile_host, requires=requires, tool_requires=tool_requires)
root_node = conan_api.graph.load_root_virtual_conanfile(requires=requires,
tool_requires=tool_requires,
profile_host=profile_host)

out.title("Computing dependency graph")
deps_graph = conan_api.graph.load_graph(root_node, profile_host=profile_host,
profile_build=profile_build, lockfile=lockfile,
remotes=remotes, update=args.update)
print_graph_basic(deps_graph)
deps_graph = conan_api.graph.load_graph_requires(requires, tool_requires,
profile_host=profile_host,
profile_build=profile_build,
lockfile=lockfile,
remotes=remotes, update=args.update)
deps_graph.report_graph_error()

out.title("Computing necessary packages")
Expand Down
15 changes: 6 additions & 9 deletions conan/cli/commands/export_pkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

from conan.api.output import cli_out_write
from conan.cli.command import conan_command
from conan.cli.common import scope_options
from conan.cli.args import add_lockfile_args, add_profiles_args, add_reference_args


Expand Down Expand Up @@ -41,14 +40,12 @@ def export_pkg(conan_api, parser, *args):
lockfile = conan_api.lockfile.update_lockfile_export(lockfile, conanfile, ref)

# TODO: Maybe we want to be able to export-pkg it as --build-require
scope_options(profile_host, requires=[ref], tool_requires=None)
root_node = conan_api.graph.load_root_virtual_conanfile(requires=[ref],
profile_host=profile_host)
deps_graph = conan_api.graph.load_graph(root_node, profile_host=profile_host,
profile_build=profile_build,
lockfile=lockfile,
remotes=None,
update=None)
deps_graph = conan_api.graph.load_graph_requires([ref], tool_requires=None,
profile_host=profile_host,
profile_build=profile_build,
lockfile=lockfile,
remotes=None,
update=None)
deps_graph.report_graph_error()
conan_api.graph.analyze_binaries(deps_graph, build_mode=[ref.name], lockfile=lockfile)
deps_graph.report_graph_error()
Expand Down
58 changes: 53 additions & 5 deletions conan/cli/commands/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
import os

from conan.api.output import ConanOutput, cli_out_write
from conan.cli.printers.graph import print_graph_packages
from conan.internal.deploy import do_deploys
from conan.cli.command import conan_command, conan_subcommand, CommandResult
from conan.cli.commands import make_abs_path
from conan.cli.commands.install import graph_compute
from conan.cli.args import common_graph_args
from conan.cli.formatters.graph import format_graph_html, format_graph_json, format_graph_dot
from conan.cli.formatters.graph.graph_info_text import format_graph_info
Expand Down Expand Up @@ -46,7 +46,29 @@ def graph_build_order(conan_api, parser, subparser, *args):
raise ConanException("Can't use --name, --version, --user or --channel arguments with "
"--requires")

deps_graph, lockfile = graph_compute(args, conan_api, partial=args.lockfile_partial)
cwd = os.getcwd()
path = conan_api.local.get_conanfile_path(args.path, cwd, py=None) if args.path else None

# Basic collaborators, remotes, lockfile, profiles
remotes = conan_api.remotes.list(args.remote)
lockfile = conan_api.lockfile.get_lockfile(lockfile=args.lockfile,
conanfile_path=path,
cwd=cwd,
partial=args.lockfile_partial)
profile_host, profile_build = conan_api.profiles.get_profiles_from_args(args)

if path:
deps_graph = conan_api.graph.load_graph_consumer(path, args.name, args.version,
args.user, args.channel,
profile_host, profile_build, lockfile,
remotes, args.build, args.update)
else:
deps_graph = conan_api.graph.load_graph_requires(args.requires, args.tool_requires,
profile_host, profile_build, lockfile,
remotes, args.build, args.update)
conan_api.graph.analyze_binaries(deps_graph, args.build, remotes=remotes, update=args.update,
lockfile=lockfile)
print_graph_packages(deps_graph)

out = ConanOutput()
out.title("Computing the build order")
Expand Down Expand Up @@ -101,12 +123,38 @@ def graph_info(conan_api, parser, subparser, *args):
if args.requires and (args.name or args.version or args.user or args.channel):
raise ConanException("Can't use --name, --version, --user or --channel arguments with "
"--requires")

if not args.path and not args.requires and not args.tool_requires:
raise ConanException("Please specify at least a path to a conanfile or a valid reference.")
if args.format is not None and (args.filter or args.package_filter):
raise ConanException("Formatted outputs cannot be filtered")

deps_graph, lockfile = graph_compute(args, conan_api, partial=args.lockfile_partial,
allow_error=True)
cwd = os.getcwd()
path = conan_api.local.get_conanfile_path(args.path, cwd, py=None) if args.path else None

# Basic collaborators, remotes, lockfile, profiles
remotes = conan_api.remotes.list(args.remote)
lockfile = conan_api.lockfile.get_lockfile(lockfile=args.lockfile,
conanfile_path=path,
cwd=cwd,
partial=args.lockfile_partial)
profile_host, profile_build = conan_api.profiles.get_profiles_from_args(args)

if path:
deps_graph = conan_api.graph.load_graph_consumer(path, args.name, args.version,
args.user, args.channel,
profile_host, profile_build, lockfile,
remotes, args.build, args.update,
allow_error=True,
check_updates=args.check_updates)
else:
deps_graph = conan_api.graph.load_graph_requires(args.requires, args.tool_requires,
profile_host, profile_build, lockfile,
remotes, args.build, args.update,
allow_error=True,
check_updates=args.check_updates)
conan_api.graph.analyze_binaries(graph, args.build, remotes=remotes, update=args.update,
lockfile=lockfile)
print_graph_packages(graph)

lockfile = conan_api.lockfile.update_lockfile(lockfile, deps_graph, args.lockfile_packages,
clean=args.lockfile_clean)
Expand Down
Loading