Skip to content
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

fix default_options propagation to test_requires/tool_requires #14340

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions conans/client/conanfile/configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@ def run_configure_method(conanfile, down_options, profile_options, ref):
elif "auto_shared_fpic" in conanfile.implements:
auto_shared_fpic_configure(conanfile)

self_options, up_options = conanfile.options.get_upstream_options(down_options, ref,
is_consumer)
result = conanfile.options.get_upstream_options(down_options, ref, is_consumer)
self_options, up_options, private_up_options = result
# self_options are the minimum to reproduce state, as defined from downstream (not profile)
conanfile.self_options = self_options
# up_options are the minimal options that should be propagated to dependencies
conanfile.up_options = up_options
conanfile.private_up_options = private_up_options

PackageType.compute_package_type(conanfile)

Expand Down
9 changes: 7 additions & 2 deletions conans/client/graph/graph_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,11 +284,16 @@ def _create_new_node(self, node, require, graph, profile_host, profile_build, gr
# options["dep"].opt=value only propagate to visible and host dependencies
# we will evaluate if necessary a potential "build_options", but recall that it is
# now possible to do "self.build_requires(..., options={k:v})" to specify it
if require.visible and context == CONTEXT_HOST:
if require.visible:
# Only visible requirements in the host context propagate options from downstream
down_options.update_options(node.conanfile.up_options)
else:
down_options = node.conanfile.up_options if require.visible else Options()
if require.visible:
down_options = node.conanfile.up_options
elif not require.build: # for requires in "host", like test_requires, pass myoptions
down_options = node.conanfile.private_up_options
else:
down_options = Options(options_values=node.conanfile.default_build_options)

self._prepare_node(new_node, profile_host, profile_build, down_options)
require.process_package_type(node, new_node)
Expand Down
1 change: 1 addition & 0 deletions conans/model/conan_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class ConanFile:
settings = None
options = None
default_options = None
default_build_options = None
package_type = None

implements = []
Expand Down
9 changes: 5 additions & 4 deletions conans/model/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,12 +373,13 @@ def get_upstream_options(self, down_options, own_ref, is_consumer):
which is the state that a package should define in order to reproduce
"""
assert isinstance(down_options, Options)
# We need to store a copy for internal propagation for test_requires and tool_requires
private_deps_options = Options()
private_deps_options._deps_package_options = self._deps_package_options.copy()
# self_options are the minimal necessary for a build-order
# TODO: check this, isn't this just a copy?
self_options = Options()
for pattern, options in down_options._deps_package_options.items():
self_options._deps_package_options.setdefault(pattern,
_PackageOptions()).update_options(options)
self_options._deps_package_options = down_options._deps_package_options.copy()

# compute now the necessary to propagate all down - self + self deps
upstream_options = Options()
Expand All @@ -395,4 +396,4 @@ def get_upstream_options(self, down_options, own_ref, is_consumer):
# not be able to do ``self.options["mydep"]`` because it will be empty. self.dependencies
# is the way to access dependencies (in other methods)
self._deps_package_options = {}
return self_options, upstream_options
return self_options, upstream_options, private_deps_options
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,16 @@ def test_test_requires():
c.assert_listed_require({"test/0.1": "Cache"}, test=True)
c.assert_listed_binary({"test/0.1": ("da39a3ee5e6b4b0d3255bfef95601890afd80709", "Cache")},
test=True)


def test_test_requires_options():
c = TestClient()
c.save({"test/conanfile.py": GenConanfile("test", "0.1").with_option("myoption", [1, 2, 3]),
"consumer/conanfile.txt": "[test_requires]\ntest/0.1\n[options]\n*:myoption=2"})
c.run("create test -o myoption=2")
c.assert_listed_binary({"test/0.1": ("a3cb1345b8297bfdffea4ef4bb1b2694c54d1d69", "Build")})

c.run("install consumer")
c.assert_listed_require({"test/0.1": "Cache"}, test=True)
c.assert_listed_binary({"test/0.1": ("a3cb1345b8297bfdffea4ef4bb1b2694c54d1d69", "Cache")},
test=True)
15 changes: 15 additions & 0 deletions conans/test/integration/graph/test_test_requires.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,18 @@ def package_info(self):
c.run("create libc")
# This used to crash due to component not defined to liba
assert "libc/0.1: Created package" in c.out


def test_test_requires_options():
""" the default_options = {} values also propagate to ``test_requires()`` """
c = TestClient()
c.save({"test/conanfile.py": GenConanfile("test", "0.1").with_option("myoption", [1, 2, 3]),
"consumer/conanfile.py": GenConanfile().with_test_requires("test/0.1")
.with_default_option("test/*:myoption", "2")})
c.run("create test -o myoption=2")
c.assert_listed_binary({"test/0.1": ("a3cb1345b8297bfdffea4ef4bb1b2694c54d1d69", "Build")})

c.run("install consumer")
c.assert_listed_require({"test/0.1": "Cache"}, test=True)
c.assert_listed_binary({"test/0.1": ("a3cb1345b8297bfdffea4ef4bb1b2694c54d1d69", "Cache")},
test=True)
39 changes: 39 additions & 0 deletions conans/test/integration/options/test_options_build_requires.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,45 @@ def build_requirements(self):
assert f"protobuf/1.0: MYOPTION: build-{build}" in c.out


def test_different_options_values_recipe_attributes():
"""
consumer -> protobuf (library)
\\--(build)-> protobuf (protoc)
protobuf by default is a static library (shared=False)
The "consumer" conanfile.py can use ``default_options`` to define protobuf:shared
"""
c = TestClient()
protobuf = textwrap.dedent("""
from conan import ConanFile
class Proto(ConanFile):
options = {"shared": [True, False]}
default_options = {"shared": False}

def package_info(self):
self.output.info("MYOPTION: {}-{}".format(self.context, self.options.shared))
""")
c.save({"conanfile.py": protobuf})
c.run("create . --name=protobuf --version=1.0")
c.run("create . --name=protobuf --version=1.0 -o protobuf/*:shared=True")

consumer_recipe = textwrap.dedent("""
from conan import ConanFile
class Consumer(ConanFile):
default_options = {{"protobuf/*:shared": {host}}}
default_build_options = {{"protobuf/*:shared": {build}}}
def requirements(self):
self.requires("protobuf/1.0")
def build_requirements(self):
self.build_requires("protobuf/1.0")
""")

for host, build in ((True, True), (True, False), (False, True), (False, False)):
c.save({"conanfile.py": consumer_recipe.format(host=host, build=build)})
c.run("install .")
assert f"protobuf/1.0: MYOPTION: host-{host}" in c.out
assert f"protobuf/1.0: MYOPTION: build-{build}" in c.out


def test_different_options_values_recipe_priority():
"""
consumer ---> mypkg ---> protobuf (library)
Expand Down
3 changes: 2 additions & 1 deletion conans/test/unittests/model/options_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,10 @@ def test_basic(self):
sut.static = True
assert "Incorrect attempt to modify option 'static'" in str(e.value)

self_options, up_options = sut.get_upstream_options(down_options, ref, False)
self_options, up_options, up_private = sut.get_upstream_options(down_options, ref, False)
assert up_options.dumps() == "zlib/2.0:other=1"
assert self_options.dumps() == "boost/1.0:static=False\nzlib/2.0:other=1"
assert up_private.dumps() == ""


class TestOptionsNone:
Expand Down