diff --git a/conan/api/model.py b/conan/api/model.py index 72a45cb2d7d..f525efe7976 100644 --- a/conan/api/model.py +++ b/conan/api/model.py @@ -1,7 +1,7 @@ import fnmatch import json -from conans.client.graph.graph import RECIPE_EDITABLE, RECIPE_CONSUMER, RECIPE_SYSTEM_TOOL, \ +from conans.client.graph.graph import RECIPE_EDITABLE, RECIPE_CONSUMER, RECIPE_PLATFORM, \ RECIPE_VIRTUAL, BINARY_SKIP, BINARY_MISSING, BINARY_INVALID from conans.errors import ConanException from conans.model.package_ref import PkgReference @@ -99,7 +99,7 @@ def load_graph(graphfile, graph_recipes=None, graph_binaries=None): remote_list.add_refs([pyref]) recipe = node["recipe"] - if recipe in (RECIPE_EDITABLE, RECIPE_CONSUMER, RECIPE_VIRTUAL, RECIPE_SYSTEM_TOOL): + if recipe in (RECIPE_EDITABLE, RECIPE_CONSUMER, RECIPE_VIRTUAL, RECIPE_PLATFORM): continue ref = node["ref"] diff --git a/conan/cli/printers/graph.py b/conan/cli/printers/graph.py index d5aa8248305..a7a67a09588 100644 --- a/conan/cli/printers/graph.py +++ b/conan/cli/printers/graph.py @@ -1,6 +1,6 @@ from conan.api.output import ConanOutput, Color, LEVEL_VERBOSE from conans.client.graph.graph import RECIPE_CONSUMER, RECIPE_VIRTUAL, CONTEXT_BUILD, BINARY_SKIP,\ - BINARY_SYSTEM_TOOL + BINARY_PLATFORM def print_graph_basic(graph): @@ -105,7 +105,7 @@ def _format_requires(title, reqs_to_print): return output.info(title, Color.BRIGHT_YELLOW) for pref, (status, remote) in sorted(reqs_to_print.items(), key=repr): - name = pref.repr_notime() if status != BINARY_SYSTEM_TOOL else str(pref.ref) + name = pref.repr_notime() if status != BINARY_PLATFORM else str(pref.ref) msg = f"{tab}{name} - {status}" if remote is not None and status != BINARY_SKIP: msg += f" ({remote.name})" diff --git a/conans/client/graph/graph.py b/conans/client/graph/graph.py index a50b47541ef..cce23757564 100644 --- a/conans/client/graph/graph.py +++ b/conans/client/graph/graph.py @@ -14,7 +14,7 @@ RECIPE_EDITABLE = "Editable" RECIPE_CONSUMER = "Consumer" # A conanfile from the user RECIPE_VIRTUAL = "Cli" # A virtual conanfile (dynamic in memory conanfile) -RECIPE_SYSTEM_TOOL = "System tool" +RECIPE_PLATFORM = "Platform" BINARY_CACHE = "Cache" BINARY_DOWNLOAD = "Download" @@ -25,7 +25,7 @@ BINARY_EDITABLE = "Editable" BINARY_EDITABLE_BUILD = "EditableBuild" BINARY_INVALID = "Invalid" -BINARY_SYSTEM_TOOL = "System tool" +BINARY_PLATFORM = "Platform" CONTEXT_HOST = "host" CONTEXT_BUILD = "build" diff --git a/conans/client/graph/graph_binaries.py b/conans/client/graph/graph_binaries.py index 7cb8097b64a..db3a9e78c21 100644 --- a/conans/client/graph/graph_binaries.py +++ b/conans/client/graph/graph_binaries.py @@ -6,8 +6,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_INVALID, BINARY_EDITABLE_BUILD, RECIPE_SYSTEM_TOOL, - BINARY_SYSTEM_TOOL) + BINARY_INVALID, BINARY_EDITABLE_BUILD, RECIPE_PLATFORM, + BINARY_PLATFORM) from conans.errors import NoRemoteAvailable, NotFoundException, \ PackageNotFoundException, conanfile_exception_formatter @@ -178,8 +178,8 @@ def _process_node(self, node, build_mode, remotes, update): if node.conanfile.info.invalid: node.binary = BINARY_INVALID return - if node.recipe == RECIPE_SYSTEM_TOOL: - node.binary = BINARY_SYSTEM_TOOL + if node.recipe == RECIPE_PLATFORM: + node.binary = BINARY_PLATFORM return if node.recipe == RECIPE_EDITABLE: diff --git a/conans/client/graph/graph_builder.py b/conans/client/graph/graph_builder.py index 7a2feb93367..7d1f6bfdbcf 100644 --- a/conans/client/graph/graph_builder.py +++ b/conans/client/graph/graph_builder.py @@ -5,7 +5,7 @@ from conans.client.conanfile.configure import run_configure_method from conans.client.graph.graph import DepsGraph, Node, CONTEXT_HOST, \ CONTEXT_BUILD, TransitiveRequirement, RECIPE_VIRTUAL, RECIPE_EDITABLE -from conans.client.graph.graph import RECIPE_SYSTEM_TOOL +from conans.client.graph.graph import RECIPE_PLATFORM from conans.client.graph.graph_error import GraphLoopError, GraphConflictError, GraphMissingError, \ GraphRuntimeError, GraphError from conans.client.graph.profile_node_definer import initialize_conanfile_profile @@ -215,24 +215,23 @@ def _resolve_recipe(self, ref, graph_lock): return new_ref, dep_conanfile, recipe_status, remote @staticmethod - def _resolved_system_tool(node, require, profile_build, profile_host, resolve_prereleases): - if node.context == CONTEXT_HOST and not require.build: # Only for DIRECT tool_requires - return - system_tool = profile_build.system_tools if node.context == CONTEXT_BUILD \ - else profile_host.system_tools - if system_tool: + def _resolved_system(node, require, profile_build, profile_host, resolve_prereleases): + profile = profile_build if node.context == CONTEXT_BUILD else profile_host + dep_type = "platform_tool_requires" if require.build else "platform_requires" + system_reqs = getattr(profile, dep_type) + if system_reqs: version_range = require.version_range - for d in system_tool: + for d in system_reqs: if require.ref.name == d.name: if version_range: if version_range.contains(d.version, resolve_prereleases): require.ref.version = d.version # resolved range is replaced by exact - return d, ConanFile(str(d)), RECIPE_SYSTEM_TOOL, None + return d, ConanFile(str(d)), RECIPE_PLATFORM, None elif require.ref.version == d.version: if d.revision is None or require.ref.revision is None or \ d.revision == require.ref.revision: require.ref.revision = d.revision - return d, ConanFile(str(d)), RECIPE_SYSTEM_TOOL, None + return d, ConanFile(str(d)), RECIPE_PLATFORM, None def _create_new_node(self, node, require, graph, profile_host, profile_build, graph_lock): if require.ref.version == "": @@ -246,13 +245,12 @@ def _create_new_node(self, node, require, graph, profile_host, profile_build, gr "host dependency") require.ref.version = transitive.require.ref.version + resolved = self._resolved_system(node, require, profile_build, profile_host, + self._resolve_prereleases) if graph_lock is not None: # Here is when the ranges and revisions are resolved graph_lock.resolve_locked(node, require, self._resolve_prereleases) - resolved = self._resolved_system_tool(node, require, profile_build, profile_host, - self._resolve_prereleases) - if resolved is None: try: # TODO: If it is locked not resolve range @@ -269,7 +267,7 @@ def _create_new_node(self, node, require, graph, profile_host, profile_build, gr if recipe_status == RECIPE_EDITABLE: recipe_metadata = os.path.join(dep_conanfile.recipe_folder, "metadata") dep_conanfile.folders.set_base_recipe_metadata(recipe_metadata) - elif recipe_status != RECIPE_SYSTEM_TOOL: + elif recipe_status != RECIPE_PLATFORM: recipe_metadata = self._cache.recipe_layout(new_ref).metadata() dep_conanfile.folders.set_base_recipe_metadata(recipe_metadata) # If the node is virtual or a test package, the require is also "root" diff --git a/conans/client/installer.py b/conans/client/installer.py index c9085272c3b..0aebc405c4e 100644 --- a/conans/client/installer.py +++ b/conans/client/installer.py @@ -7,7 +7,7 @@ from conans.client.conanfile.package import run_package_method from conans.client.generators import write_generators from conans.client.graph.graph import BINARY_BUILD, BINARY_CACHE, BINARY_DOWNLOAD, BINARY_EDITABLE, \ - BINARY_SYSTEM_TOOL, BINARY_UPDATE, BINARY_EDITABLE_BUILD, BINARY_SKIP + BINARY_PLATFORM, BINARY_UPDATE, BINARY_EDITABLE_BUILD, BINARY_SKIP from conans.client.graph.install_graph import InstallGraph from conans.client.source import retrieve_exports_sources, config_source from conans.errors import (ConanException, conanfile_exception_formatter, conanfile_remove_attr) @@ -294,7 +294,7 @@ def _download_pkg(self, package): self._remote_manager.get_package(node.pref, node.binary_remote) def _handle_package(self, package, install_reference, handled_count, total_count): - if package.binary == BINARY_SYSTEM_TOOL: + if package.binary == BINARY_PLATFORM: return if package.binary in (BINARY_EDITABLE, BINARY_EDITABLE_BUILD): diff --git a/conans/client/profile_loader.py b/conans/client/profile_loader.py index 5b1a4c451d6..097c18c1566 100644 --- a/conans/client/profile_loader.py +++ b/conans/client/profile_loader.py @@ -5,6 +5,7 @@ from jinja2 import Environment, FileSystemLoader from conan import conan_version +from conan.api.output import ConanOutput from conan.internal.api import detect_api from conan.internal.cache.home_paths import HomePaths from conan.tools.env.environment import ProfileEnvironment @@ -218,8 +219,10 @@ class _ProfileValueParser(object): @staticmethod def get_profile(profile_text, base_profile=None): # Trying to strip comments might be problematic if things contain # - doc = ConfigParser(profile_text, allowed_fields=["tool_requires", "system_tools", - "settings", + doc = ConfigParser(profile_text, allowed_fields=["tool_requires", + "system_tools", # DEPRECATED: platform_tool_requires + "platform_requires", + "platform_tool_requires", "settings", "options", "conf", "buildenv", "runenv"]) # Parse doc sections into Conan model, Settings, Options, etc @@ -227,11 +230,13 @@ 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) + doc_platform_requires = doc.platform_requires or "" + doc_platform_tool_requires = doc.platform_tool_requires or doc.system_tools or "" if doc.system_tools: - system_tools = [RecipeReference.loads(r.strip()) - for r in doc.system_tools.splitlines() if r.strip()] - else: - system_tools = [] + ConanOutput().warning("Profile [system_tools] is deprecated," + " please use [platform_tool_requires]") + platform_tool_requires = [RecipeReference.loads(r) for r in doc_platform_tool_requires.splitlines()] + platform_requires = [RecipeReference.loads(r) for r in doc_platform_requires.splitlines()] if doc.conf: conf = ConfDefinition() @@ -243,9 +248,12 @@ def get_profile(profile_text, base_profile=None): # Create or update the profile base_profile = base_profile or Profile() - current_system_tools = {r.name: r for r in base_profile.system_tools} - current_system_tools.update({r.name: r for r in system_tools}) - base_profile.system_tools = list(current_system_tools.values()) + current_platform_tool_requires = {r.name: r for r in base_profile.platform_tool_requires} + current_platform_tool_requires.update({r.name: r for r in platform_tool_requires}) + base_profile.platform_tool_requires = list(current_platform_tool_requires.values()) + current_platform_requires = {r.name: r for r in base_profile.platform_requires} + current_platform_requires.update({r.name: r for r in platform_requires}) + base_profile.platform_requires = list(current_platform_requires.values()) base_profile.settings.update(settings) for pkg_name, values_dict in package_settings.items(): diff --git a/conans/model/dependencies.py b/conans/model/dependencies.py index e001141cc8b..c65d4648b4c 100644 --- a/conans/model/dependencies.py +++ b/conans/model/dependencies.py @@ -1,6 +1,6 @@ from collections import OrderedDict -from conans.client.graph.graph import RECIPE_SYSTEM_TOOL +from conans.client.graph.graph import RECIPE_PLATFORM from conans.errors import ConanException from conans.model.recipe_ref import RecipeReference from conans.model.conanfile_interface import ConanFileInterface @@ -82,7 +82,7 @@ def from_node(node): for require, transitive in node.transitive_deps.items()) return ConanFileDependencies(d) - def filter(self, require_filter, remove_system_tools=False): + def filter(self, require_filter, remove_system=True): # FIXME: Copy of hte above, to return ConanFileDependencies class object def filter_fn(require): for k, v in require_filter.items(): @@ -91,10 +91,10 @@ def filter_fn(require): return True data = OrderedDict((k, v) for k, v in self._data.items() if filter_fn(k)) - if remove_system_tools: + if remove_system: data = OrderedDict((k, v) for k, v in data.items() # TODO: Make "recipe" part of ConanFileInterface model - if v._conanfile._conan_node.recipe != RECIPE_SYSTEM_TOOL) + if v._conanfile._conan_node.recipe != RECIPE_PLATFORM) return ConanFileDependencies(data, require_filter) def transitive_requires(self, other): @@ -133,7 +133,7 @@ def direct_host(self): @property def direct_build(self): - return self.filter({"build": True, "direct": True}, remove_system_tools=True) + return self.filter({"build": True, "direct": True}) @property def host(self): @@ -147,7 +147,7 @@ def test(self): @property def build(self): - return self.filter({"build": True}, remove_system_tools=True) + return self.filter({"build": True}) def get_transitive_requires(consumer, dependency): diff --git a/conans/model/profile.py b/conans/model/profile.py index a06ea9efc4c..df78bb9ba64 100644 --- a/conans/model/profile.py +++ b/conans/model/profile.py @@ -17,7 +17,8 @@ def __init__(self): self.package_settings = defaultdict(OrderedDict) self.options = Options() self.tool_requires = OrderedDict() # ref pattern: list of ref - self.system_tools = [] + self.platform_tool_requires = [] + self.platform_requires = [] self.conf = ConfDefinition() self.buildenv = ProfileEnvironment() self.runenv = ProfileEnvironment() @@ -74,9 +75,13 @@ def dumps(self): for pattern, req_list in self.tool_requires.items(): result.append("%s: %s" % (pattern, ", ".join(str(r) for r in req_list))) - if self.system_tools: - result.append("[system_tools]") - result.extend(str(t) for t in self.system_tools) + if self.platform_tool_requires: + result.append("[platform_tool_requires]") + result.extend(str(t) for t in self.platform_tool_requires) + + if self.platform_requires: + result.append("[platform_requires]") + result.extend(str(t) for t in self.platform_requires) if self.conf: result.append("[conf]") @@ -115,9 +120,13 @@ def compose_profile(self, other): existing[r.name] = req self.tool_requires[pattern] = list(existing.values()) - current_system_tools = {r.name: r for r in self.system_tools} - current_system_tools.update({r.name: r for r in other.system_tools}) - self.system_tools = list(current_system_tools.values()) + current_platform_tool_requires = {r.name: r for r in self.platform_tool_requires} + current_platform_tool_requires.update({r.name: r for r in other.platform_tool_requires}) + self.platform_tool_requires = list(current_platform_tool_requires.values()) + current_platform_requires = {r.name: r for r in self.platform_requires} + current_platform_requires.update({r.name: r for r in other.platform_requires}) + self.platform_requires = list(current_platform_requires.values()) + self.conf.update_conf_definition(other.conf) self.buildenv.update_profile_env(other.buildenv) # Profile composition, last has priority self.runenv.update_profile_env(other.runenv) diff --git a/conans/test/integration/graph/test_platform_requires.py b/conans/test/integration/graph/test_platform_requires.py new file mode 100644 index 00000000000..486b6d0a5d4 --- /dev/null +++ b/conans/test/integration/graph/test_platform_requires.py @@ -0,0 +1,214 @@ +import json +import os +import textwrap + +import pytest + +from conans.test.assets.genconanfile import GenConanfile +from conans.test.utils.tools import TestClient +from conans.util.files import save + + +class TestPlatformRequires: + def test_platform_requires(self): + client = TestClient() + client.save({"conanfile.py": GenConanfile("pkg", "1.0").with_requires("dep/1.0"), + "profile": "[platform_requires]\ndep/1.0"}) + client.run("create . -pr=profile") + assert "dep/1.0 - Platform" in client.out + + def test_platform_requires_non_matching(self): + """ if what is specified in [platform_requires] doesn't match what the recipe requires, then + the platform_requires will not be used, and the recipe will use its declared version + """ + client = TestClient() + client.save({"dep/conanfile.py": GenConanfile("dep", "1.0"), + "conanfile.py": GenConanfile("pkg", "1.0").with_requires("dep/1.0"), + "profile": "[platform_requires]\ndep/1.1"}) + client.run("create dep") + client.run("create . -pr=profile") + assert "dep/1.0#6a99f55e933fb6feeb96df134c33af44 - Cache" in client.out + + def test_platform_requires_range(self): + client = TestClient() + client.save({"conanfile.py": GenConanfile("pkg", "1.0").with_requires("dep/[>=1.0]"), + "profile": "[platform_requires]\ndep/1.1"}) + client.run("create . -pr=profile") + assert "dep/1.1 - Platform" in client.out + + def test_platform_requires_range_non_matching(self): + """ if what is specified in [platform_requires] doesn't match what the recipe requires, then + the platform_requires will not be used, and the recipe will use its declared version + """ + client = TestClient() + client.save({"dep/conanfile.py": GenConanfile("dep", "1.1"), + "conanfile.py": GenConanfile("pkg", "1.0").with_requires("dep/[>=1.0]"), + "profile": "[platform_requires]\ndep/0.1"}) + client.run("create dep") + client.run("create . -pr=profile") + assert "dep/1.1#af79f1e3973b7d174e7465038c3f5f36 - Cache" in client.out + + def test_platform_requires_no_host(self): + """ + platform_requires must not affect tool-requires + """ + client = TestClient() + client.save({"conanfile.py": GenConanfile("pkg", "1.0").with_tool_requires("dep/1.0"), + "profile": "[platform_requires]\ndep/1.0"}) + client.run("create . -pr=profile", assert_error=True) + assert "ERROR: Package 'dep/1.0' not resolved: No remote defined" in client.out + + def test_graph_info_platform_requires_range(self): + """ + graph info doesn't crash + """ + client = TestClient() + client.save({"conanfile.py": GenConanfile("pkg", "1.0").with_requires("dep/[>=1.0]"), + "profile": "[platform_requires]\ndep/1.1"}) + client.run("graph info . -pr=profile") + assert "dep/1.1 - Platform" in client.out + + def test_consumer_resolved_version(self): + client = TestClient() + conanfile = textwrap.dedent(""" + from conan import ConanFile + class Pkg(ConanFile): + requires = "dep/[>=1.0]" + + def generate(self): + for r, _ in self.dependencies.items(): + self.output.info(f"DEPENDENCY {r.ref}") + """) + client.save({"conanfile.py": conanfile, + "profile": "[platform_requires]\ndep/1.1"}) + client.run("install . -pr=profile") + assert "dep/1.1 - Platform" in client.out + assert "conanfile.py: DEPENDENCY dep/1.1" in client.out + + def test_consumer_resolved_revision(self): + client = TestClient() + conanfile = textwrap.dedent(""" + from conan import ConanFile + class Pkg(ConanFile): + requires = "dep/1.1" + + def generate(self): + for r, _ in self.dependencies.items(): + self.output.info(f"DEPENDENCY {repr(r.ref)}") + """) + client.save({"conanfile.py": conanfile, + "profile": "[platform_requires]\ndep/1.1#rev1"}) + client.run("install . -pr=profile") + assert "dep/1.1 - Platform" in client.out + assert "conanfile.py: DEPENDENCY dep/1.1#rev1" in client.out + + conanfile = textwrap.dedent(""" + from conan import ConanFile + class Pkg(ConanFile): + requires = "dep/1.1#rev1" + + def generate(self): + for r, _ in self.dependencies.items(): + self.output.info(f"DEPENDENCY {repr(r.ref)}") + """) + client.save({"conanfile.py": conanfile}) + client.run("install . -pr=profile") + assert "dep/1.1 - Platform" in client.out + assert "conanfile.py: DEPENDENCY dep/1.1#rev1" in client.out + + def test_consumer_unresolved_revision(self): + """ if a recipe specifies an exact revision and so does the profile + and it doesn't match, it is an error + """ + client = TestClient() + conanfile = textwrap.dedent(""" + from conan import ConanFile + class Pkg(ConanFile): + requires = "dep/1.1#rev2" + + def generate(self): + for r, _ in self.dependencies.items(): + self.output.info(f"DEPENDENCY {repr(r.ref)}") + """) + client.save({"conanfile.py": conanfile, + "profile": "[platform_requires]\ndep/1.1#rev1"}) + client.run("install . -pr=profile", assert_error=True) + assert "ERROR: Package 'dep/1.1' not resolved" in client.out + + +class TestPlatformRequiresLock: + + def test_platform_requires_range(self): + c = TestClient() + c.save({"conanfile.py": GenConanfile("pkg", "1.0").with_requires("dep/[>=1.0]"), + "profile": "[platform_requires]\ndep/1.1"}) + c.run("lock create . -pr=profile") + assert "dep/1.1 - Platform" in c.out + lock = json.loads(c.load("conan.lock")) + assert lock["requires"] == ["dep/1.1"] + + c.run("install .", assert_error=True) + assert "Package 'dep/1.1' not resolved: No remote defined" in c.out + c.run("install . -pr=profile") + assert "dep/1.1 - Platform" in c.out + + # if the profile points to another version it is an error, not in the lockfile + c.save({"profile": "[platform_requires]\ndep/1.2"}) + c.run("install . -pr=profile", assert_error=True) + assert "ERROR: Requirement 'dep/1.2' not in lockfile" in c.out + + +class TestGenerators: + def test_platform_requires_range(self): + client = TestClient() + conanfile = textwrap.dedent(""" + from conan import ConanFile + class Pkg(ConanFile): + settings = "build_type" + requires = "dep/[>=1.0]" + generators = "CMakeDeps", # "PkgConfigDeps" + """) + client.save({"conanfile.py": conanfile, + "profile": "[platform_requires]\ndep/1.1"}) + client.run("install . -pr=profile") + assert "dep/1.1 - Platform" in client.out + assert not os.path.exists(os.path.join(client.current_folder, "dep-config.cmake")) + assert not os.path.exists(os.path.join(client.current_folder, "dep.pc")) + + +class TestPackageID: + """ if a consumer depends on recipe revision or package_id what happens + """ + + @pytest.mark.parametrize("package_id_mode", ["recipe_revision_mode", "full_package_mode"]) + def test_package_id_modes(self, package_id_mode): + """ this test validates that the computation of the downstream consumers package_id + doesn't break even if it depends on fields not existing in upstream platform_requires, like revision + or package_id + """ + client = TestClient() + save(client.cache.new_config_path, f"core.package_id:default_build_mode={package_id_mode}") + client.save({"conanfile.py": GenConanfile("pkg", "1.0").with_requires("dep/1.0"), + "profile": "[platform_requires]\ndep/1.0"}) + client.run("create . -pr=profile") + assert "dep/1.0 - Platform" in client.out + + def test_package_id_explicit_revision(self): + """ + Changing the platform_requires revision affects consumers if package_revision_mode=recipe_revision + """ + client = TestClient() + save(client.cache.new_config_path, "core.package_id:default_build_mode=recipe_revision_mode") + client.save({"conanfile.py": GenConanfile("pkg", "1.0").with_requires("dep/1.0"), + "profile": "[platform_requires]\ndep/1.0#r1", + "profile2": "[platform_requires]\ndep/1.0#r2"}) + client.run("create . -pr=profile") + assert "dep/1.0#r1 - Platform" in client.out + assert "pkg/1.0#7ed9bbd2a7c3c4381438c163c93a9f21:" \ + "abfcc78fa8242cabcd1e3d92896aa24808c789a3 - Build" in client.out + + client.run("create . -pr=profile2") + # pkg gets a new package_id because it is a different revision + assert "dep/1.0#r2 - Platform" in client.out + assert "pkg/1.0#7ed9bbd2a7c3c4381438c163c93a9f21:" \ + "abfcc78fa8242cabcd1e3d92896aa24808c789a3 - Build" in client.out diff --git a/conans/test/integration/graph/test_system_tools.py b/conans/test/integration/graph/test_system_tools.py index 0e5b2799742..f5d101d1554 100644 --- a/conans/test/integration/graph/test_system_tools.py +++ b/conans/test/integration/graph/test_system_tools.py @@ -13,9 +13,9 @@ class TestToolRequires: def test_system_tool_require(self): client = TestClient() client.save({"conanfile.py": GenConanfile("pkg", "1.0").with_tool_requires("tool/1.0"), - "profile": "[system_tools]\ntool/1.0"}) + "profile": "[platform_tool_requires]\ntool/1.0"}) client.run("create . -pr=profile") - assert "tool/1.0 - System tool" in client.out + assert "tool/1.0 - Platform" in client.out def test_system_tool_require_non_matching(self): """ if what is specified in [system_tool_require] doesn't match what the recipe requires, then @@ -27,14 +27,15 @@ def test_system_tool_require_non_matching(self): "profile": "[system_tools]\ntool/1.1"}) client.run("create tool") client.run("create . -pr=profile") + assert "WARN: Profile [system_tools] is deprecated" in client.out assert "tool/1.0#60ed6e65eae112df86da7f6d790887fd - Cache" in client.out def test_system_tool_require_range(self): client = TestClient() client.save({"conanfile.py": GenConanfile("pkg", "1.0").with_tool_requires("tool/[>=1.0]"), - "profile": "[system_tools]\ntool/1.1"}) + "profile": "[platform_tool_requires]\ntool/1.1"}) client.run("create . -pr=profile") - assert "tool/1.1 - System tool" in client.out + assert "tool/1.1 - Platform" in client.out def test_system_tool_require_range_non_matching(self): """ if what is specified in [system_tool_require] doesn't match what the recipe requires, then @@ -43,7 +44,7 @@ def test_system_tool_require_range_non_matching(self): client = TestClient() client.save({"tool/conanfile.py": GenConanfile("tool", "1.1"), "conanfile.py": GenConanfile("pkg", "1.0").with_tool_requires("tool/[>=1.0]"), - "profile": "[system_tools]\ntool/0.1"}) + "profile": "[platform_tool_requires]\ntool/0.1"}) client.run("create tool") client.run("create . -pr=profile") assert "tool/1.1#888bda2348dd2ddcf5960d0af63b08f7 - Cache" in client.out @@ -54,7 +55,7 @@ def test_system_tool_require_no_host(self): """ client = TestClient() client.save({"conanfile.py": GenConanfile("pkg", "1.0").with_requires("tool/1.0"), - "profile": "[system_tools]\ntool/1.0"}) + "profile": "[platform_tool_requires]\ntool/1.0"}) client.run("create . -pr=profile", assert_error=True) assert "ERROR: Package 'tool/1.0' not resolved: No remote defined" in client.out @@ -64,9 +65,9 @@ def test_graph_info_system_tool_require_range(self): """ client = TestClient() client.save({"conanfile.py": GenConanfile("pkg", "1.0").with_tool_requires("tool/[>=1.0]"), - "profile": "[system_tools]\ntool/1.1"}) + "profile": "[platform_tool_requires]\ntool/1.1"}) client.run("graph info . -pr=profile") - assert "tool/1.1 - System tool" in client.out + assert "tool/1.1 - Platform" in client.out def test_consumer_resolved_version(self): client = TestClient() @@ -80,9 +81,9 @@ def generate(self): self.output.info(f"DEPENDENCY {r.ref}") """) client.save({"conanfile.py": conanfile, - "profile": "[system_tools]\ntool/1.1"}) + "profile": "[platform_tool_requires]\ntool/1.1"}) client.run("install . -pr=profile") - assert "tool/1.1 - System tool" in client.out + assert "tool/1.1 - Platform" in client.out assert "conanfile.py: DEPENDENCY tool/1.1" in client.out def test_consumer_resolved_revision(self): @@ -97,9 +98,9 @@ def generate(self): self.output.info(f"DEPENDENCY {repr(r.ref)}") """) client.save({"conanfile.py": conanfile, - "profile": "[system_tools]\ntool/1.1#rev1"}) + "profile": "[platform_tool_requires]\ntool/1.1#rev1"}) client.run("install . -pr=profile") - assert "tool/1.1 - System tool" in client.out + assert "tool/1.1 - Platform" in client.out assert "conanfile.py: DEPENDENCY tool/1.1#rev1" in client.out conanfile = textwrap.dedent(""" @@ -113,7 +114,7 @@ def generate(self): """) client.save({"conanfile.py": conanfile}) client.run("install . -pr=profile") - assert "tool/1.1 - System tool" in client.out + assert "tool/1.1 - Platform" in client.out assert "conanfile.py: DEPENDENCY tool/1.1#rev1" in client.out def test_consumer_unresolved_revision(self): @@ -131,7 +132,7 @@ def generate(self): self.output.info(f"DEPENDENCY {repr(r.ref)}") """) client.save({"conanfile.py": conanfile, - "profile": "[system_tools]\ntool/1.1#rev1"}) + "profile": "[platform_tool_requires]\ntool/1.1#rev1"}) client.run("install . -pr=profile", assert_error=True) assert "ERROR: Package 'tool/1.1' not resolved" in client.out @@ -141,21 +142,29 @@ class TestToolRequiresLock: def test_system_tool_require_range(self): c = TestClient() c.save({"conanfile.py": GenConanfile("pkg", "1.0").with_tool_requires("tool/[>=1.0]"), - "profile": "[system_tools]\ntool/1.1"}) + "profile": "[platform_tool_requires]\ntool/1.1"}) c.run("lock create . -pr=profile") - assert "tool/1.1 - System tool" in c.out + assert "tool/1.1 - Platform" in c.out lock = json.loads(c.load("conan.lock")) assert lock["build_requires"] == ["tool/1.1"] c.run("install .", assert_error=True) assert "Package 'tool/1.1' not resolved: No remote defined" in c.out c.run("install . -pr=profile") - assert "tool/1.1 - System tool" in c.out + assert "tool/1.1 - Platform" in c.out - # even if the profile points to another version the locked one will prevail - c.save({"profile": "[system_tools]\ntool/1.2"}) + # even if we create a version within the range, it will error if not matching the profile + c.save({"tool/conanfile.py": GenConanfile("tool", "1.1")}) + c.run("create tool") + # if the profile points to another version it is an error, not in the lockfile + c.save({"profile": "[platform_tool_requires]\ntool/1.2"}) c.run("install . -pr=profile", assert_error=True) - assert "Package 'tool/1.1' not resolved: No remote defined" in c.out + assert "ERROR: Requirement 'tool/1.2' not in lockfile" in c.out + + # if we relax the lockfile, we can still resolve to the platform_tool_requires + # specified by the profile + c.run("install . -pr=profile --lockfile-partial") + assert "tool/1.2 - Platform" in c.out class TestGenerators: @@ -178,9 +187,9 @@ def generate(self): deps.generate() """) client.save({"conanfile.py": conanfile, - "profile": "[system_tools]\ntool/1.1"}) + "profile": "[platform_tool_requires]\ntool/1.1"}) client.run("install . -pr=profile") - assert "tool/1.1 - System tool" in client.out + assert "tool/1.1 - Platform" in client.out assert not os.path.exists(os.path.join(client.current_folder, "tool-config.cmake")) assert not os.path.exists(os.path.join(client.current_folder, "tool.pc")) @@ -198,9 +207,9 @@ def test_package_id_modes(self, package_id_mode): client = TestClient() save(client.cache.new_config_path, f"core.package_id:default_build_mode={package_id_mode}") client.save({"conanfile.py": GenConanfile("pkg", "1.0").with_tool_requires("dep/1.0"), - "profile": "[system_tools]\ndep/1.0"}) + "profile": "[platform_tool_requires]\ndep/1.0"}) client.run("create . -pr=profile") - assert "dep/1.0 - System tool" in client.out + assert "dep/1.0 - Platform" in client.out def test_package_id_explicit_revision(self): """ @@ -209,15 +218,15 @@ def test_package_id_explicit_revision(self): client = TestClient() save(client.cache.new_config_path, "core.package_id:default_build_mode=recipe_revision_mode") client.save({"conanfile.py": GenConanfile("pkg", "1.0").with_tool_requires("dep/1.0"), - "profile": "[system_tools]\ndep/1.0#r1", - "profile2": "[system_tools]\ndep/1.0#r2"}) + "profile": "[platform_tool_requires]\ndep/1.0#r1", + "profile2": "[platform_tool_requires]\ndep/1.0#r2"}) client.run("create . -pr=profile") - assert "dep/1.0#r1 - System tool" in client.out + assert "dep/1.0#r1 - Platform" in client.out assert "pkg/1.0#27a56f09310cf1237629bae4104fe5bd:" \ "ea0e320d94b4b70fcb3efbabf9ab871542f8f696 - Build" in client.out client.run("create . -pr=profile2") # pkg gets a new package_id because it is a different revision - assert "dep/1.0#r2 - System tool" in client.out + assert "dep/1.0#r2 - Platform" in client.out assert "pkg/1.0#27a56f09310cf1237629bae4104fe5bd:" \ "334882884da082740e5a002a0b6fdb509a280159 - Build" in client.out diff --git a/conans/test/integration/metadata/test_metadata_collect.py b/conans/test/integration/metadata/test_metadata_collect.py index 0a7310777e4..ab74eafc983 100644 --- a/conans/test/integration/metadata/test_metadata_collect.py +++ b/conans/test/integration/metadata/test_metadata_collect.py @@ -171,7 +171,7 @@ def test_custom_command_collect(): c.save({"dep/conanfile.py": conanfile.format(name="dep", requires="tool_requires = 'tool/0.1'"), "pkg/conanfile.py": conanfile.format(name="pkg", requires='requires = "dep/0.1"'), - "profile": "[system_tools]\ntool/0.1"}) + "profile": "[platform_tool_requires]\ntool/0.1"}) c.run("create dep -pr=profile") c.run("create pkg -pr=profile") c.run("metadata:collect --requires=pkg/0.1 --metadata=* --metadata-remote=default -pr=profile") diff --git a/conans/test/unittests/client/profile_loader/profile_loader_test.py b/conans/test/unittests/client/profile_loader/profile_loader_test.py index 3b7f4c77656..22e4e875f11 100644 --- a/conans/test/unittests/client/profile_loader/profile_loader_test.py +++ b/conans/test/unittests/client/profile_loader/profile_loader_test.py @@ -109,24 +109,24 @@ def save_profile(txt, name): RecipeReference.loads("two/1.2@lasote/stable")]} -def test_profile_compose_system_tools(): +def test_profile_compose_platform_tool_requires(): tmp = temp_folder() - save(os.path.join(tmp, "profile0"), "[system_tools]\ntool1/1.0") - save(os.path.join(tmp, "profile1"), "[system_tools]\ntool2/2.0") - save(os.path.join(tmp, "profile2"), "include(./profile0)\n[system_tools]\ntool3/3.0") - save(os.path.join(tmp, "profile3"), "include(./profile0)\n[system_tools]\ntool1/1.1") + save(os.path.join(tmp, "profile0"), "[platform_tool_requires]\ntool1/1.0") + save(os.path.join(tmp, "profile1"), "[platform_tool_requires]\ntool2/2.0") + save(os.path.join(tmp, "profile2"), "include(./profile0)\n[platform_tool_requires]\ntool3/3.0") + save(os.path.join(tmp, "profile3"), "include(./profile0)\n[platform_tool_requires]\ntool1/1.1") profile_loader = ProfileLoader(cache_folder=temp_folder()) # If not used cache, will not error profile2 = profile_loader.load_profile("./profile2", tmp) - assert profile2.system_tools == [RecipeReference.loads("tool1/1.0"), - RecipeReference.loads("tool3/3.0")] + assert profile2.platform_tool_requires == [RecipeReference.loads("tool1/1.0"), + RecipeReference.loads("tool3/3.0")] profile3 = profile_loader.load_profile("./profile3", tmp) - assert profile3.system_tools == [RecipeReference.loads("tool1/1.1")] + assert profile3.platform_tool_requires == [RecipeReference.loads("tool1/1.1")] profile0 = profile_loader.load_profile("./profile0", tmp) profile1 = profile_loader.load_profile("./profile1", tmp) profile0.compose_profile(profile1) - assert profile0.system_tools == [RecipeReference.loads("tool1/1.0"), - RecipeReference.loads("tool2/2.0")] + assert profile0.platform_tool_requires == [RecipeReference.loads("tool1/1.0"), + RecipeReference.loads("tool2/2.0")] def test_profile_compose_tool_requires():