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][poc] Checking the [system_tools] idea #10166

Merged
merged 14 commits into from
Feb 15, 2023
2 changes: 2 additions & 0 deletions conans/client/graph/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
RECIPE_CONSUMER = "Consumer" # A conanfile from the user
RECIPE_VIRTUAL = "Virtual" # A virtual conanfile (dynamic in memory conanfile)
RECIPE_MISSING = "Missing recipe" # Impossible to find a recipe for this reference
RECIPE_DEFERRED = "Deferred"

BINARY_CACHE = "Cache"
BINARY_DOWNLOAD = "Download"
Expand All @@ -25,6 +26,7 @@
BINARY_UNKNOWN = "Unknown"
BINARY_INVALID = "Invalid"
BINARY_ERROR = "ConfigurationError"
BINARY_DEFERRED = "Deferred"

CONTEXT_HOST = "host"
CONTEXT_BUILD = "build"
Expand Down
7 changes: 6 additions & 1 deletion conans/client/graph/graph_binaries.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
from conans.client.graph.graph import (BINARY_BUILD, BINARY_CACHE, BINARY_DOWNLOAD, BINARY_MISSING,
BINARY_UPDATE, RECIPE_EDITABLE, BINARY_EDITABLE,
RECIPE_CONSUMER, RECIPE_VIRTUAL, BINARY_SKIP, BINARY_UNKNOWN,
BINARY_INVALID, BINARY_ERROR)
BINARY_INVALID, BINARY_ERROR, RECIPE_DEFERRED,
BINARY_DEFERRED)
from conans.errors import NoRemoteAvailable, NotFoundException, \
PackageNotFoundException, conanfile_exception_formatter
from conans.model.info import PACKAGE_ID_UNKNOWN, PACKAGE_ID_INVALID
Expand Down Expand Up @@ -164,6 +165,10 @@ def _process_node(self, node, pref, build_mode):
node.binary = BINARY_EDITABLE # TODO: PREV?
return

if node.recipe == RECIPE_DEFERRED:
node.binary = BINARY_DEFERRED
return

if pref.package_id == PACKAGE_ID_INVALID:
# annotate pattern, so unused patterns in --build are not displayed as errors
build_mode.forced(node.conanfile, node.ref)
Expand Down
37 changes: 25 additions & 12 deletions conans/client/graph/graph_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
import fnmatch
from collections import deque

from conans import ConanFile
from conans.client.conanfile.configure import run_configure_method
from conans.client.graph.graph import DepsGraph, Node, RECIPE_EDITABLE, CONTEXT_HOST, \
CONTEXT_BUILD, RECIPE_CONSUMER, TransitiveRequirement
CONTEXT_BUILD, RECIPE_CONSUMER, TransitiveRequirement, RECIPE_DEFERRED
from conans.client.graph.graph_error import GraphError
from conans.client.graph.profile_node_definer import initialize_conanfile_profile
from conans.client.graph.provides import check_graph_provides
Expand Down Expand Up @@ -227,25 +228,37 @@ def _resolve_recipe(self, ref, graph_lock):

return new_ref, dep_conanfile, recipe_status, remote

@staticmethod
def _resolved_deferred(node, require, profile_build, profile_host):
deferred = profile_build.deferred_requires if node.context == CONTEXT_BUILD \
else profile_host.deferred_requires
if deferred:
for d in deferred:
if require.ref == d:
return d, ConanFile(None, str(d)), RECIPE_DEFERRED, None

def _create_new_node(self, node, require, graph, profile_host, profile_build, graph_lock):
try:
# TODO: If it is locked not resolve range
# if not require.locked_id: # if it is locked, nothing to resolved
# TODO: This range-resolve might resolve in a given remote or cache
# Make sure next _resolve_recipe use it
resolved_ref = self._resolver.resolve(require, str(node.ref))
context = CONTEXT_BUILD if require.build else node.context

# This accounts for alias too
resolved = self._resolve_recipe(resolved_ref, graph_lock)
except ConanException as e:
raise GraphError.missing(node, require, str(e))
resolved = self._resolved_deferred(node, require, profile_build, profile_host)
if resolved is None:
try:
# TODO: If it is locked not resolve range
# if not require.locked_id: # if it is locked, nothing to resolved
# TODO: This range-resolve might resolve in a given remote or cache
# Make sure next _resolve_recipe use it
resolved_ref = self._resolver.resolve(require, str(node.ref))

# This accounts for alias too
resolved = self._resolve_recipe(resolved_ref, graph_lock)
except ConanException as e:
raise GraphError.missing(node, require, str(e))

new_ref, dep_conanfile, recipe_status, remote = resolved

initialize_conanfile_profile(dep_conanfile, profile_build, profile_host, node.context,
require.build, new_ref)

context = CONTEXT_BUILD if require.build else node.context
new_node = Node(new_ref, dep_conanfile, context=context)
new_node.recipe = recipe_status
new_node.remote = remote
Expand Down
4 changes: 3 additions & 1 deletion conans/client/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from conans.client.file_copier import report_copied_files
from conans.client.generators import write_generators
from conans.client.graph.graph import BINARY_BUILD, BINARY_CACHE, BINARY_DOWNLOAD, BINARY_EDITABLE, \
BINARY_MISSING, BINARY_UPDATE, BINARY_UNKNOWN
BINARY_MISSING, BINARY_UPDATE, BINARY_UNKNOWN, BINARY_DEFERRED
from conans.client.graph.install_graph import InstallGraph, raise_missing
from conans.client.importer import remove_imports, run_imports
from conans.client.source import retrieve_exports_sources, config_source
Expand Down Expand Up @@ -330,6 +330,8 @@ def _download_pkg(self, package):
self._remote_manager.get_package(node.conanfile, node.pref, node.binary_remote)

def _handle_package(self, package, install_reference, build_mode):
if package.binary == BINARY_DEFERRED:
return
if package.binary == BINARY_EDITABLE:
self._handle_node_editable(package)
return
Expand Down
9 changes: 8 additions & 1 deletion conans/client/profile_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ class _ProfileValueParser(object):
"""
@staticmethod
def get_profile(profile_text, base_profile=None):
doc = ConfigParser(profile_text, allowed_fields=["tool_requires",
doc = ConfigParser(profile_text, allowed_fields=["tool_requires", "deferred_requires",
"settings", "env",
"options", "conf", "buildenv"])

Expand All @@ -233,6 +233,12 @@ def get_profile(profile_text, base_profile=None):
options = Options.loads(doc.options) if doc.options else None
tool_requires = _ProfileValueParser._parse_tool_requires(doc)

if doc.deferred_requires:
deferred_requires = [RecipeReference.loads(r.strip())
for r in doc.deferred_requires.splitlines() if r.strip()]
else:
deferred_requires = []

if doc.conf:
conf = ConfDefinition()
conf.loads(doc.conf, profile=True)
Expand All @@ -242,6 +248,7 @@ def get_profile(profile_text, base_profile=None):

# Create or update the profile
base_profile = base_profile or Profile()
base_profile.deferred_requires = deferred_requires
base_profile.settings.update(settings)
for pkg_name, values_dict in package_settings.items():
base_profile.package_settings[pkg_name].update(values_dict)
Expand Down
5 changes: 3 additions & 2 deletions conans/model/dependencies.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from collections import OrderedDict

from conans.client.graph.graph import BINARY_SKIP
from conans.client.graph.graph import BINARY_SKIP, BINARY_DEFERRED
from conans.errors import ConanException
from conans.model.recipe_ref import RecipeReference
from conans.model.conanfile_interface import ConanFileInterface
Expand Down Expand Up @@ -78,7 +78,8 @@ def from_node(node):
# TODO: Probably the BINARY_SKIP should be filtered later at the user level, not forced here
d = OrderedDict((require, ConanFileInterface(transitive.node.conanfile))
for require, transitive in node.transitive_deps.items()
if transitive.node.binary != BINARY_SKIP)
if transitive.node.binary != BINARY_SKIP and
transitive.node.binary != BINARY_DEFERRED)
return ConanFileDependencies(d)

def filter(self, require_filter):
Expand Down
2 changes: 2 additions & 0 deletions conans/model/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def __init__(self):
self.package_settings = defaultdict(OrderedDict)
self.options = Options()
self.tool_requires = OrderedDict() # ref pattern: list of ref
self.deferred_requires = []
self.conf = ConfDefinition()
self.buildenv = ProfileEnvironment()

Expand Down Expand Up @@ -99,6 +100,7 @@ def compose_profile(self, other):
existing[r.name] = req
self.tool_requires[pattern] = list(existing.values())

self.deferred_requires = self.deferred_requires + other.deferred_requires # FIXME repetitions
self.conf.update_conf_definition(other.conf)
self.buildenv.update_profile_env(other.buildenv) # Profile composition, last has priority

Expand Down
11 changes: 11 additions & 0 deletions conans/test/integration/graph/test_deferred.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

from conans.test.assets.genconanfile import GenConanfile
from conans.test.utils.tools import TestClient


def test_deferred():
client = TestClient()
client.save({"conanfile.py": GenConanfile().with_tool_requires("tool/1.0"),
"profile": "[deferred_requires]\ntool/1.0"})
client.run("create . pkg/1.0@ -pr=profile")
assert "tool/1.0:357add7d387f11a959f3ee7d4fc9c2487dbaa604 - Deferred" in client.out