diff --git a/conans/client/conanfile/configure.py b/conans/client/conanfile/configure.py index 210e0f5f1bd..653655418cd 100644 --- a/conans/client/conanfile/configure.py +++ b/conans/client/conanfile/configure.py @@ -33,9 +33,11 @@ def run_configure_method(conanfile, down_options, profile_options, ref): PackageType.compute_package_type(conanfile) - conanfile.build_requires = BuildRequirements(conanfile.requires) - conanfile.test_requires = TestRequirements(conanfile.requires) - conanfile.tool_requires = ToolRequirements(conanfile.requires) + conanfile._conan_add_requirement_allowed = True + + conanfile.build_requires = BuildRequirements(conanfile) + conanfile.test_requires = TestRequirements(conanfile) + conanfile.tool_requires = ToolRequirements(conanfile) if hasattr(conanfile, "requirements"): with conanfile_exception_formatter(conanfile, "requirements"): @@ -44,3 +46,5 @@ def run_configure_method(conanfile, down_options, profile_options, ref): if hasattr(conanfile, "build_requirements"): with conanfile_exception_formatter(conanfile, "build_requirements"): conanfile.build_requirements() + + delattr(conanfile, "_conan_add_requirement_allowed") diff --git a/conans/model/conan_file.py b/conans/model/conan_file.py index 554d0938afa..bd9b771a1f2 100644 --- a/conans/model/conan_file.py +++ b/conans/model/conan_file.py @@ -94,7 +94,7 @@ def __init__(self, display_name=""): self.generators = [self.generators] if isinstance(self.settings, str): self.settings = [self.settings] - self.requires = Requirements(self.requires, self.build_requires, self.test_requires, + self.requires = Requirements(self, self.requires, self.build_requires, self.test_requires, self.tool_requires) self.options = Options(self.options or {}, self.default_options) diff --git a/conans/model/requires.py b/conans/model/requires.py index 50b7739f8dc..d952b362d95 100644 --- a/conans/model/requires.py +++ b/conans/model/requires.py @@ -387,42 +387,52 @@ def deduce_package_id_mode(self, pkg_type, dep_node, non_embed_mode, embed_mode, class BuildRequirements: # Just a wrapper around requires for backwards compatibility with self.build_requires() syntax - def __init__(self, requires): - self._requires = requires + def __init__(self, conanfile): + self._conanfile = conanfile + self._requires = conanfile.requires def __call__(self, ref, package_id_mode=None, visible=False, run=None, options=None, override=None): # TODO: Check which arguments could be user-defined + if not getattr(self._conanfile, "_conan_add_requirement_allowed", False): + self._conanfile.output.warning("build_requires() should only be declared in requirements()/build_requirements()", warn_tag="misplaced_requirement") self._requires.build_require(ref, package_id_mode=package_id_mode, visible=visible, run=run, options=options, override=override) class ToolRequirements: # Just a wrapper around requires for backwards compatibility with self.build_requires() syntax - def __init__(self, requires): - self._requires = requires + def __init__(self, conanfile): + self._conanfile = conanfile + self._requires = conanfile.requires def __call__(self, ref, package_id_mode=None, visible=False, run=True, options=None, override=None): # TODO: Check which arguments could be user-defined + if not getattr(self._conanfile, "_conan_add_requirement_allowed", False): + self._conanfile.output.warning("tool_requires() should only be declared in requirements()/build_requirements()", warn_tag="misplaced_requirement") self._requires.tool_require(ref, package_id_mode=package_id_mode, visible=visible, run=run, options=options, override=override) class TestRequirements: # Just a wrapper around requires for backwards compatibility with self.build_requires() syntax - def __init__(self, requires): - self._requires = requires + def __init__(self, conanfile): + self._conanfile = conanfile + self._requires = conanfile.requires def __call__(self, ref, run=None, options=None, force=None): + if not getattr(self._conanfile, "_conan_add_requirement_allowed", False): + self._conanfile.output.warning("test_requires() should only be declared in requirements()/build_requirements()", warn_tag="misplaced_requirement") self._requires.test_require(ref, run=run, options=options, force=force) class Requirements: """ User definitions of all requires in a conanfile """ - def __init__(self, declared=None, declared_build=None, declared_test=None, + def __init__(self, conanfile, declared=None, declared_build=None, declared_test=None, declared_build_tool=None): + self._conanfile = conanfile self._requires = OrderedDict() # Construct from the class definitions if declared is not None: @@ -486,6 +496,8 @@ def values(self): # TODO: Plan the interface for smooth transition from 1.X def __call__(self, str_ref, **kwargs): + if not getattr(self._conanfile, "_conan_add_requirement_allowed", False): + self._conanfile.output.warning("requires() should only be declared in requirements()/build_requirements()", warn_tag="misplaced_requirement") if str_ref is None: return assert isinstance(str_ref, str) diff --git a/conans/test/integration/build_requires/build_requires_test.py b/conans/test/integration/build_requires/build_requires_test.py index da3a5af71e4..6c46906e481 100644 --- a/conans/test/integration/build_requires/build_requires_test.py +++ b/conans/test/integration/build_requires/build_requires_test.py @@ -581,3 +581,18 @@ def test_build_missing_build_requires(): c.run("install app --build=missing") assert "- Build" not in c.out assert re.search(r"Skipped binaries(\s*)tool/0.1, tooldep/0.1", c.out) + + +def test_requirement_in_wrong_method(): + tc = TestClient() + tc.save({"conanfile.py": textwrap.dedent(""" + from conan import ConanFile + class Pkg(ConanFile): + name = "pkg" + version = "0.1" + def configure(self): + self.requires("foo/1.0") + """)}) + tc.run('create . -cc="core:warnings_as_errors=[\'*\']"', assert_error=True) + assert "ERROR: misplaced_requirement: requires() should only be declared in requirements()/build_requirements()" in tc.out +