Skip to content

Commit 03c26aa

Browse files
authoredNov 20, 2024
Fix/cli consumer (conan-io#17346)
1 parent fddc08c commit 03c26aa

File tree

11 files changed

+108
-29
lines changed

11 files changed

+108
-29
lines changed
 

‎conan/internal/deploy.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def do_deploys(conan_api, graph, deploy, deploy_package, deploy_folder):
5757
conanfile = node.conanfile
5858
if not conanfile.ref: # virtual or conanfile.txt, can't have deployer
5959
continue
60-
consumer = conanfile._conan_is_consumer
60+
consumer = conanfile._conan_is_consumer # noqa
6161
if any(conanfile.ref.matches(p, consumer) for p in excluded):
6262
continue
6363
if not any(conanfile.ref.matches(p, consumer) for p in included):

‎conan/internal/methods.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def run_configure_method(conanfile, down_options, profile_options, ref):
9393
auto_language(conanfile) # default implementation removes `compiler.cstd`
9494

9595
# Assign only the current package options values, but none of the dependencies
96-
is_consumer = conanfile._conan_is_consumer
96+
is_consumer = conanfile._conan_is_consumer # noqa
9797
conanfile.options.apply_downstream(down_options, profile_options, ref, is_consumer)
9898

9999
if hasattr(conanfile, "configure"):

‎conan/tools/cmake/cmakedeps/cmakedeps.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def content(self):
105105
msg.append(f" find_package({config.file_name})")
106106
targets = ' '.join(c.root_target_name for c in direct_configs)
107107
msg.append(f" target_link_libraries(... {targets})")
108-
if self._conanfile._conan_is_consumer:
108+
if self._conanfile._conan_is_consumer: # noqa
109109
self._conanfile.output.info("\n".join(msg), fg=Color.CYAN)
110110
else:
111111
self._conanfile.output.verbose("\n".join(msg))

‎conan/tools/cmake/cmakedeps2/cmakedeps.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,7 @@ def _print_help(self, direct_deps):
8585
targets.append(target_name or f"{dep.ref.name}::{dep.ref.name}")
8686
if targets:
8787
msg.append(f" target_link_libraries(... {' '.join(targets)})")
88-
if self._conanfile._conan_is_consumer:
89-
self._conanfile.output.info("\n".join(msg), fg=Color.CYAN)
90-
else:
91-
self._conanfile.output.verbose("\n".join(msg))
88+
self._conanfile.output.info("\n".join(msg), fg=Color.CYAN)
9289

9390
def set_property(self, dep, prop, value, build_context=False):
9491
"""

‎conans/client/graph/build_mode.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def forced(self, conan_file, ref, with_deps_to_build=False):
6363
# TODO: ref can be obtained from conan_file
6464

6565
for pattern in self._excluded_patterns:
66-
if ref_matches(ref, pattern, is_consumer=conan_file._conan_is_consumer):
66+
if ref_matches(ref, pattern, is_consumer=conan_file._conan_is_consumer): # noqa
6767
conan_file.output.info("Excluded build from source")
6868
return False
6969

@@ -82,7 +82,7 @@ def forced(self, conan_file, ref, with_deps_to_build=False):
8282

8383
# Patterns to match, if package matches pattern, build is forced
8484
for pattern in self.patterns:
85-
if ref_matches(ref, pattern, is_consumer=conan_file._conan_is_consumer):
85+
if ref_matches(ref, pattern, is_consumer=conan_file._conan_is_consumer): # noqa
8686
return True
8787
return False
8888

@@ -109,7 +109,7 @@ def allowed_compatible(self, conanfile):
109109
return True # If it has not been excluded by the negated patterns, it is included
110110

111111
for pattern in self._build_compatible_patterns:
112-
if ref_matches(conanfile.ref, pattern, is_consumer=conanfile._conan_is_consumer):
112+
if ref_matches(conanfile.ref, pattern, is_consumer=conanfile._conan_is_consumer): # noqa
113113
return True
114114

115115
def should_build_missing(self, conanfile):
@@ -120,5 +120,5 @@ def should_build_missing(self, conanfile):
120120
return True # If it has not been excluded by the negated patterns, it is included
121121

122122
for pattern in self.build_missing_patterns:
123-
if ref_matches(conanfile.ref, pattern, is_consumer=conanfile._conan_is_consumer):
123+
if ref_matches(conanfile.ref, pattern, is_consumer=conanfile._conan_is_consumer): # noqa
124124
return True

‎conans/client/graph/graph_builder.py

+14-8
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from conan.internal.cache.conan_reference_layout import BasicLayout
66
from conan.internal.methods import run_configure_method
77
from conans.client.graph.graph import DepsGraph, Node, CONTEXT_HOST, \
8-
CONTEXT_BUILD, TransitiveRequirement, RECIPE_VIRTUAL, RECIPE_EDITABLE
8+
CONTEXT_BUILD, TransitiveRequirement, RECIPE_VIRTUAL, RECIPE_EDITABLE, RECIPE_CONSUMER
99
from conans.client.graph.graph import RECIPE_PLATFORM
1010
from conans.client.graph.graph_error import GraphLoopError, GraphConflictError, GraphMissingError, \
1111
GraphRuntimeError, GraphError
@@ -39,7 +39,9 @@ def load_graph(self, root_node, profile_host, profile_build, graph_lock=None):
3939
# print("Loading graph")
4040
dep_graph = DepsGraph()
4141

42-
self._prepare_node(root_node, profile_host, profile_build, Options())
42+
is_test_package = getattr(root_node.conanfile, "tested_reference_str", None)
43+
define_consumers = root_node.recipe == RECIPE_VIRTUAL or is_test_package
44+
self._prepare_node(root_node, profile_host, profile_build, Options(), define_consumers)
4345
rs = self._initialize_requires(root_node, dep_graph, graph_lock, profile_build, profile_host)
4446
dep_graph.add_node(root_node)
4547

@@ -171,19 +173,25 @@ def _conflicting_refs(ref1, ref2):
171173
raise GraphConflictError(node, require, prev_node, prev_require, base_previous)
172174

173175
@staticmethod
174-
def _prepare_node(node, profile_host, profile_build, down_options):
175-
176+
def _prepare_node(node, profile_host, profile_build, down_options, define_consumers=False):
176177
# basic node configuration: calling configure() and requirements()
177178
conanfile, ref = node.conanfile, node.ref
178179

179180
profile_options = profile_host.options if node.context == CONTEXT_HOST else profile_build.options
180181
assert isinstance(profile_options, Options), type(profile_options)
181182
run_configure_method(conanfile, down_options, profile_options, ref)
182183

184+
if define_consumers: # Mark this requirements as the "consumers" nodes
185+
tested_ref = getattr(conanfile, "tested_reference_str", None)
186+
tested_ref = RecipeReference.loads(tested_ref) if tested_ref else None
187+
for r in conanfile.requires.values():
188+
if tested_ref is None or r.ref == tested_ref:
189+
r.is_consumer = True
190+
183191
# Apply build_tools_requires from profile, overriding the declared ones
184192
profile = profile_host if node.context == CONTEXT_HOST else profile_build
185193
for pattern, tool_requires in profile.tool_requires.items():
186-
if ref_matches(ref, pattern, is_consumer=conanfile._conan_is_consumer):
194+
if ref_matches(ref, pattern, is_consumer=conanfile._conan_is_consumer): # noqa
187195
for tool_require in tool_requires: # Do the override
188196
if str(tool_require) == str(ref): # FIXME: Ugly str comparison
189197
continue # avoid self-loop of build-requires in build context
@@ -364,9 +372,7 @@ def _create_new_node(self, node, require, graph, profile_host, profile_build, gr
364372

365373
new_ref = layout.reference
366374
dep_conanfile.folders.set_base_recipe_metadata(layout.metadata()) # None for platform_xxx
367-
# If the node is virtual or a test package, the require is also "root"
368-
is_test_package = getattr(node.conanfile, "tested_reference_str", False)
369-
if node.conanfile._conan_is_consumer and (node.recipe == RECIPE_VIRTUAL or is_test_package):
375+
if getattr(require, "is_consumer", None):
370376
dep_conanfile._conan_is_consumer = True
371377
initialize_conanfile_profile(dep_conanfile, profile_build, profile_host, node.context,
372378
require.build, new_ref, parent=node.conanfile)

‎conans/client/graph/profile_node_definer.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ def initialize_conanfile_profile(conanfile, profile_build, profile_host, base_co
3131
else:
3232
_initialize_conanfile(conanfile, profile_host, settings_host, ref)
3333
# Host profile with some build profile information
34-
conanfile.buildenv_build = profile_build.buildenv.get_profile_env(ref, conanfile._conan_is_consumer)
35-
conanfile.conf_build = profile_build.conf.get_conanfile_conf(ref, conanfile._conan_is_consumer)
34+
is_consumer = conanfile._conan_is_consumer # noqa
35+
conanfile.buildenv_build = profile_build.buildenv.get_profile_env(ref, is_consumer)
36+
conanfile.conf_build = profile_build.conf.get_conanfile_conf(ref, is_consumer)
3637
conanfile.settings_build = settings_build
3738
conanfile.settings_target = None
3839

@@ -60,7 +61,7 @@ def _per_package_settings(conanfile, profile, ref):
6061
pkg_settings = []
6162

6263
for pattern, settings in package_settings_values.items():
63-
if ref_matches(ref, pattern, conanfile._conan_is_consumer):
64+
if ref_matches(ref, pattern, conanfile._conan_is_consumer): # noqa
6465
pkg_settings.extend(settings)
6566

6667
if pkg_settings:
@@ -80,7 +81,8 @@ def _initialize_conanfile(conanfile, profile, settings, ref):
8081
conanfile.settings._frozen = True
8182
conanfile._conan_buildenv = profile.buildenv
8283
conanfile._conan_runenv = profile.runenv
83-
conanfile.conf = profile.conf.get_conanfile_conf(ref, conanfile._conan_is_consumer) # Maybe this can be done lazy too
84+
# Maybe this can be done lazy too
85+
conanfile.conf = profile.conf.get_conanfile_conf(ref, conanfile._conan_is_consumer) # noqa
8486

8587

8688
def consumer_definer(conanfile, profile_host, profile_build):

‎conans/client/loader.py

+3-6
Original file line numberDiff line numberDiff line change
@@ -171,11 +171,8 @@ def load_consumer(self, conanfile_path, name=None, version=None, user=None,
171171
else:
172172
conanfile.display_name = os.path.basename(conanfile_path)
173173
conanfile.output.scope = conanfile.display_name
174-
try:
175-
conanfile._conan_is_consumer = True
176-
return conanfile
177-
except Exception as e: # re-raise with file name
178-
raise ConanException("%s: %s" % (conanfile_path, str(e)))
174+
conanfile._conan_is_consumer = True
175+
return conanfile
179176

180177
def load_conanfile(self, conanfile_path, ref, graph_lock=None, remotes=None,
181178
update=None, check_update=None):
@@ -396,7 +393,7 @@ def _get_required_conan_version_without_loading(conan_file_path):
396393
found = re.search(r"(.*)required_conan_version\s*=\s*[\"'](.*)[\"']", contents)
397394
if found and "#" not in found.group(1):
398395
txt_version = found.group(2)
399-
except:
396+
except: # noqa this should be solid, cannot fail
400397
pass
401398

402399
return txt_version

‎test/integration/conanfile/conanfile_errors_test.py

+24
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import pytest
55

6+
from conan.test.assets.genconanfile import GenConanfile
67
from conan.test.utils.tools import TestClient
78

89

@@ -216,3 +217,26 @@ def package_info(self):
216217
else:
217218
assert f"The {property_name} property is undefined because {property_name}s is empty." in c.out
218219

220+
221+
def test_consumer_unexpected():
222+
tc = TestClient(light=True)
223+
cmake_conanfile = textwrap.dedent("""
224+
from conan import ConanFile
225+
226+
class CMake(ConanFile):
227+
name = "cmake"
228+
version = "1.0"
229+
package_type = "application"
230+
231+
def package_info(self):
232+
self.output.info("cmake/1.0 -> " + str(self._conan_is_consumer))
233+
""")
234+
tc.save({
235+
"cmake/conanfile.py": cmake_conanfile,
236+
"dep1/conanfile.py": GenConanfile("dep1", "1.0"),
237+
"conanfile.py": GenConanfile("pkg", "1.0").with_requires("dep1/1.0"),
238+
"profile": "include(default)\n[tool_requires]\n*: cmake/1.0\n"})
239+
tc.run("export dep1")
240+
tc.run("create cmake")
241+
tc.run("create . -b=missing -pr=profile")
242+
assert "cmake/1.0 -> True" not in tc.out

‎test/integration/configuration/profile_test.py

+24
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,30 @@ def configure(self):
557557
assert "I'm None and my build type is Debug" in client.out
558558

559559

560+
def test_package_consumer_is_only_the_tested_one():
561+
"""
562+
the &:xxx pattern applies only to the package being tested, not other requires
563+
"""
564+
c = TestClient(light=True)
565+
test = textwrap.dedent("""
566+
from conan import ConanFile
567+
class Tool(ConanFile):
568+
def requirements(self):
569+
self.requires(self.tested_reference_str)
570+
self.requires("dep/1.0")
571+
572+
def test(self):
573+
pass
574+
""")
575+
c.save({"dep/conanfile.py": GenConanfile("dep", "1.0"),
576+
"pkg/conanfile.py": GenConanfile("pkg", "0.1").with_option("myoption", [1, 2]),
577+
"pkg/test_package/conanfile.py": test})
578+
c.run("create dep")
579+
c.run("create pkg -o &:myoption=1")
580+
# This would crash if myoption is applied to dep
581+
assert 'pkg/0.1 (test package): Running test()' in c.out
582+
583+
560584
def test_consumer_specific_settings_from_profile():
561585
client = TestClient()
562586
conanfile = str(GenConanfile().with_settings("build_type").with_name("hello"))

‎test/integration/cross_building/build_requires_from_profile_test.py

+29
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import os
12
import unittest
23
import textwrap
34

@@ -260,3 +261,31 @@ def package_info(self):
260261
self.assertIn("gtest/1.0: Build OS=Linux", t.out)
261262
self.assertIn("gtest/1.0: PackageInfo OS=Linux", t.out)
262263
self.assertIn("library/version: Build OS=Linux", t.out)
264+
265+
266+
def test_consumer_get_profile_tool_requires():
267+
# [tool_requires] defined in a profile will result in applying them in the
268+
# consumer, both if using a conanfile.txt of using a --requires
269+
t = TestClient(light=True)
270+
tool = textwrap.dedent("""
271+
from conan import ConanFile
272+
class Pkg(ConanFile):
273+
name = "tool"
274+
version = "1.0"
275+
def package_info(self):
276+
self.buildenv_info.define("MYVAR", "MYVALUE")
277+
""")
278+
t.save({'profile': "[tool_requires]\ntool/1.0",
279+
'dep.py': GenConanfile("dep", "1.0"),
280+
'tool.py': tool,
281+
'conanfile.txt': ""})
282+
t.run("create tool.py")
283+
t.run("create dep.py")
284+
t.run("install . --profile ./profile")
285+
env = t.load("conanbuildenv.sh")
286+
assert 'export MYVAR="MYVALUE"' in env
287+
os.remove(os.path.join(t.current_folder, "conanbuildenv.sh"))
288+
# also when using --requires
289+
t.run("install --requires=dep/1.0 --profile ./profile")
290+
env = t.load("conanbuildenv.sh")
291+
assert 'export MYVAR="MYVALUE"' in env

0 commit comments

Comments
 (0)
Please sign in to comment.