diff --git a/conan/api/subapi/graph.py b/conan/api/subapi/graph.py index 14693da43df..b99ac8cfbf7 100644 --- a/conan/api/subapi/graph.py +++ b/conan/api/subapi/graph.py @@ -87,8 +87,6 @@ def load_root_test_conanfile(self, path, tested_reference, profile_host, profile def _load_root_virtual_conanfile(self, profile_host, profile_build, requires, tool_requires, lockfile, remotes, update, check_updates=False, python_requires=None): - if not python_requires and not requires and not tool_requires: - raise ConanException("Provide requires or tool_requires") loader = self._helpers.loader conanfile = loader.load_virtual(requires=requires, tool_requires=tool_requires, diff --git a/conan/cli/commands/run.py b/conan/cli/commands/run.py index 70007250d6f..5398eafffec 100644 --- a/conan/cli/commands/run.py +++ b/conan/cli/commands/run.py @@ -34,6 +34,14 @@ def run(conan_api, parser, *args): # Default values for install setattr(args, "output_folder", tmpdir) setattr(args, "generator", []) + # If there is no conanfile in the cwd and no --requires/--tool-requires, + # use an in-memory virtual conanfile so that a profile [tool_requires] + # section is enough and executables from them can be executed + if args.path == "." and not args.requires and not args.tool_requires \ + and not os.path.isfile(os.path.join(cwd, "conanfile.py")) \ + and not os.path.isfile(os.path.join(cwd, "conanfile.txt")): + args.path = None + args.tool_requires = [] try: deps_graph, lockfile, _ = _run_install_command(conan_api, args, cwd, return_install_error=False) diff --git a/test/integration/command/test_run.py b/test/integration/command/test_run.py index 7a56eb371db..a1a4c7f9181 100644 --- a/test/integration/command/test_run.py +++ b/test/integration/command/test_run.py @@ -103,3 +103,36 @@ def test_run_status_is_propagated(client): client.run("run false --requires=pkg/0.1", assert_error=True) assert "Error installing the dependencies" not in client.out assert "ERROR: Error 1 while executing" in client.out + + +def test_run_no_conanfile_profile_tool_requires(): + # https://github.com/conan-io/conan/issues/20206 + # When no conanfile exists in cwd and no --requires/--tool-requires is + # given, a profile [tool_requires] alone should be enough to drive the + # graph. Previously this failed with "Conanfile not found". + tc = TestClient() + conanfile = textwrap.dedent(""" + from conan import ConanFile + from conan.tools.files import save + import os + + class Pkg(ConanFile): + name = "pkg" + version = "0.1" + package_type = "application" + + def package(self): + exe = os.path.join(self.package_folder, "bin", '""" + executable + """') + save(self, exe, "echo Hello World!") + os.chmod(exe, 0o755) + """) + tc.save({"pkg/conanfile.py": conanfile}) + tc.run("create pkg") + profile = textwrap.dedent(""" + include(default) + [tool_requires] + pkg/0.1 + """) + tc.save({"myprofile": profile}, clean_first=True) + tc.run(f"run {executable} -pr:h=myprofile") + assert "Hello World!" in tc.out