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
[question] Hot to propagate tool_requires
dependency to consumer packages.
#15143
Comments
Hi @lmglmg Thanks for your questions.
What is possible is to inject |
@memsharded , I understand that However, if someone writes I also had a situation where I packaged a script into a conan package, and this script depends on a tool, and whoever uses the script also needs to use the tool, but in the build context. I ended up making all my dependencies define both tool and script in their requirements, but this is very ugly and not scalable to more than a handful of packages. |
There are some ways to indicate that a tool_requires need something more, but not for building itself, using regular requires. Example: $ conan install --tool-requires=meson/[*] -g VirtualBuildEnv
$ meson --version
'meson' is not recognized as an internal or external command,
operable program or batch file.
$ ninja --version
'ninja' is not recognized as an internal or external command,
operable program or batch file.
$ conanbuild.bat
$ meson --version
Meson works correctly only with python 3.7+.
You have python 3.6.8 (tags/v3.6.8:3c6b436a57, Dec 24 2018, 00:16:47) [MSC v.1916 64 bit (AMD64)].
Please update your environment
$ninja --version
1.11.1 That is, the def requirements(self):
...
self.requires("ninja/1.11.1") The key is that when you are already in the "build" context, you don't need to |
This test also proves that a class TestToolRequiresTransitivity:
def test_basic(self):
c = TestClient()
tool = textwrap.dedent(r"""
import os
from conan import ConanFile
from conan.tools.files import chdir, save
class Tool(ConanFile):
name = "tool"
version = "0.1"
def package(self):
with chdir(self, self.package_folder):
echo = f"@echo off\necho MY-TOOL! {self.name}/{self.version}!!"
save(self, "bin/mytool.bat", echo)
save(self, "bin/mytool.sh", echo)
os.chmod("bin/mytool.sh", 0o777)
""")
pkg = textwrap.dedent("""
from conan import ConanFile
class Pkg(ConanFile):
name = "pkg"
version = "0.1"
def requirements(self):
self.requires("tool/0.1", build=True, visible=True, headers=False, libs=False,
run=True)
""")
app = textwrap.dedent("""
import platform
from conan import ConanFile
class App(ConanFile):
requires = "pkg/0.1"
def build(self):
cmd = "mytool.bat" if platform.system() == "Windows" else "mytool.sh"
self.run(cmd)
""")
c.save({"tool/conanfile.py": tool,
"pkg/conanfile.py": pkg,
"app/conanfile.py": app})
c.run("create tool")
c.run("create pkg")
c.run("build app")
print(c.out)
assert "MY-TOOL! tool/0.1!!" in c.out |
This only works if I do not use a If I use a In
However, no additional CMake files were generated and the configure step fails with the following error:
I'll try to make a minimal example with the |
Just in case it helps, |
I tried calling |
You need to define |
But doesn't that mean that this is needed in every downstream consumer? This appears to be tedious, especially if the tool is actually used internally by the script provided by |
It shouldn't. And there are ways to do similar things as the example above proves. What doesn't work is trying to use that with a build system (CMake) It is possible that I am missing something, the best would be to materialize the use case in a test like the one I provided above in However, it might be possible that we are trying to push |
@memsharded , I've modified your test so that it demonstrates the problem: import textwrap
import platform
import pytest
from conans.test.utils.tools import TestClient
class TestTransitiveRequiresInBuildContext:
@pytest.mark.skipif(platform.system() == 'Windows', reason="Needs bash support")
def test_basic(self):
c = TestClient()
tool = textwrap.dedent(r"""
import os
from conan import ConanFile
from conan.tools.files import chdir, save
class Tool(ConanFile):
name = "tool"
version = "0.1"
def package(self):
with chdir(self, self.package_folder):
echo = f"#!/bin/bash\n@echo off\necho MY-TOOL! {self.name}/{self.version}!!"
save(self, "bin/mytool", echo)
os.chmod("bin/mytool", 0o777)
""")
pkg = textwrap.dedent("""
import textwrap
from conan import ConanFile
from conan.tools.files import chdir, save
class Pkg(ConanFile):
name = "pkg"
version = "0.1"
def requirements(self):
self.requires("tool/0.1", build=True, visible=True, headers=False, libs=False,
run=True)
def package(self):
with chdir(self, self.package_folder):
cmake_script = textwrap.dedent('''
include_guard( GLOBAL )
find_program( mytool_var "mytool" )
if ( NOT mytool_var )
message( FATAL_ERROR "Cannot find mytool" )
else()
execute_process( COMMAND ${mytool_var} )
endif()
''')
save(self, "cmake_helper.cmake", cmake_script)
def package_id(self):
self.info.clear()
def package_info(self):
self.cpp_info.libdirs = []
self.cpp_info.set_property('cmake_build_modules', [
'cmake_helper.cmake',
])
""")
app_cmakelists = textwrap.dedent("""
cmake_minimum_required( VERSION 3.25 )
project( testApp )
find_package( pkg REQUIRED )
""")
app = textwrap.dedent("""
import textwrap
from conan import ConanFile
from conan.tools.cmake import cmake_layout, CMake, CMakeDeps, CMakeToolchain
class Pkg(ConanFile):
requires = 'pkg/0.1'
settings = 'os', 'arch', 'build_type', 'compiler'
def layout(self):
cmake_layout(self)
def generate(self):
tc = CMakeToolchain(self)
tc.generate()
deps = CMakeDeps(self)
deps.generate()
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
""")
c.save({
"tool/conanfile.py": tool,
"pkg/conanfile.py": pkg,
"app/CMakeLists.txt": app_cmakelists,
"app/conanfile.py": app,
})
c.run("create tool")
c.run("create pkg")
c.run("build app")
print(c.out)
assert "MY-TOOL! tool/0.1!!" in c.out As you can see, nobody never uses |
This happens because the script is reused from a def test_basic(self):
c = TestClient()
tool = textwrap.dedent(r"""
import os
from conan import ConanFile
from conan.tools.files import chdir, save
class Tool(ConanFile):
name = "tool"
version = "0.1"
def package(self):
with chdir(self, self.package_folder):
echo = f"@echo off\necho MY-TOOL! {self.name}/{self.version}!!"
save(self, "bin/mytool.bat", echo)
save(self, "bin/mytool.sh", echo)
os.chmod("bin/mytool.sh", 0o777)
""")
pkg = textwrap.dedent("""
import textwrap
from conan import ConanFile
from conan.tools.files import chdir, save
class Pkg(ConanFile):
name = "pkg"
version = "0.1"
def requirements(self):
self.requires("tool/0.1", build=True, visible=True, headers=False, libs=False,
run=True)
def package(self):
with chdir(self, self.package_folder):
cmake_script = textwrap.dedent('''
include_guard( GLOBAL )
message(STATUS "MYCMAKE SCRIPT!!!!")
find_program( mytool_var "mytool.bat" )
if ( NOT mytool_var )
message( FATAL_ERROR "Cannot find mytool" )
else()
execute_process( COMMAND ${mytool_var} )
endif()
''')
save(self, "cmake_helper.cmake", cmake_script)
def package_id(self):
self.info.clear()
def package_info(self):
self.cpp_info.libdirs = []
self.cpp_info.set_property('cmake_build_modules', [
'cmake_helper.cmake',
])
""")
app_cmakelists = textwrap.dedent("""
cmake_minimum_required( VERSION 3.25 )
project( testApp )
find_package( pkg REQUIRED )
""")
app = textwrap.dedent("""
import textwrap
from conan import ConanFile
from conan.tools.cmake import cmake_layout, CMake, CMakeDeps, CMakeToolchain
class Pkg(ConanFile):
tool_requires = 'pkg/0.1'
settings = 'os', 'arch', 'build_type', 'compiler'
def layout(self):
cmake_layout(self)
def generate(self):
tc = CMakeToolchain(self)
tc.generate()
deps = CMakeDeps(self)
deps.build_context_activated=["pkg"]
deps.build_context_build_modules=["pkg"]
deps.generate()
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
""")
c.save({
"tool/conanfile.py": tool,
"pkg/conanfile.py": pkg,
"app/CMakeLists.txt": app_cmakelists,
"app/conanfile.py": app,
})
c.run("create tool")
c.run("create pkg")
c.run("build app")
print(c.out)
assert "MY-TOOL! tool/0.1!!" in c.out Otherwise, no, it is not possible to have Note that your defined pkg = textwrap.dedent("""
import textwrap
from conan import ConanFile
from conan.tools.files import chdir, save
class Pkg(ConanFile):
name = "pkg"
version = "0.1"
def requirements(self):
self.requires("tool/0.1", build=True, visible=True, headers=False, libs=False,
run=True)
def package(self):
with chdir(self, self.package_folder):
cmake_script = textwrap.dedent('''
include_guard( GLOBAL )
message(STATUS "INCLUDED CMAKE SXEIPT!!!!!!")
execute_process( COMMAND mytool.bat)
''')
save(self, "cmake_helper.cmake", cmake_script)
def package_id(self):
self.info.clear()
def package_info(self):
self.cpp_info.builddirs = ["."]
self.cpp_info.libdirs = []
""")
app_cmakelists = textwrap.dedent("""
cmake_minimum_required( VERSION 3.25 )
project( testApp )
include("cmake_helper")
""") It is the |
Hi @memsharded , unfortunately, this deps.build_context_activated=["pkg"]
deps.build_context_build_modules=["pkg"] does not work for us because it requires that every single package that uses As @lmglmg already described, our use case is the following:
Our current workaround that @lmglmg created is to have a bootstrap part in cmake code that literally invokes This works, but:
|
What is your question?
I have a forked
gtest
conan package which uses a custom tool when definingctest
targets (add_test
will have this tool as a parameter ). This custom tool is built using conan. It has apackage_type = 'application'
set.Using this tool with
self.tool_requires('the_tool/[~1]@mycompany/main')
works well and fine when buildinggtest
itself, but the issue is that I also need to specify in the consumers of my forkedgtest
package this build dependency.If I do not specify this
tool_requires
dependency, the binary cannot be found because it will not be located in thePATH
.How can I tell to my consumers that they should use the
build_requires
for this package also. This is an implementation detail in a way, and setting thistool_requires
to all consumers is cumbersome.I am also building for emscripten platform, where this tool is not build as an emscripten application, but must be available to
ctest
as an host application for the device on which the test is ran.Is there an easy way to transitively propagate the
tool_requires
dependency?Have you read the CONTRIBUTING guide?
The text was updated successfully, but these errors were encountered: