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

Add implements attribute in ConanFile to provide opt-in automatic management of some options and settings. #14320

Merged
Merged
Show file tree
Hide file tree
Changes from all 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: 0 additions & 5 deletions conan/tools/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
from conans.model.build_info import CppInfo as _CppInfo
from conan.tools.helpers import (
default_config_options,
default_configure,
default_package_id
)


def CppInfo(conanfile):
Expand Down
10 changes: 5 additions & 5 deletions conans/client/conanfile/configure.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from conans.errors import conanfile_exception_formatter
from conans.model.pkg_type import PackageType
from conans.model.requires import BuildRequirements, TestRequirements, ToolRequirements
from conan.tools import default_config_options, default_configure
from conans.client.conanfile.implementations import auto_shared_fpic_config_options, auto_shared_fpic_configure


def run_configure_method(conanfile, down_options, profile_options, ref):
Expand All @@ -10,8 +10,8 @@ def run_configure_method(conanfile, down_options, profile_options, ref):
if hasattr(conanfile, "config_options"):
with conanfile_exception_formatter(conanfile, "config_options"):
conanfile.config_options()
else:
default_config_options(conanfile)
elif "auto_shared_fpic" in conanfile.implements:
auto_shared_fpic_config_options(conanfile)

# Assign only the current package options values, but none of the dependencies
is_consumer = conanfile._conan_is_consumer
Expand All @@ -20,8 +20,8 @@ def run_configure_method(conanfile, down_options, profile_options, ref):
if hasattr(conanfile, "configure"):
with conanfile_exception_formatter(conanfile, "configure"):
conanfile.configure()
else:
default_configure(conanfile)
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)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
from conans.model.pkg_type import PackageType


def default_config_options(conanfile):
def auto_shared_fpic_config_options(conanfile):
if conanfile.settings.get_safe("os") == "Windows":
conanfile.options.rm_safe("fPIC")


def default_configure(conanfile):
def auto_shared_fpic_configure(conanfile):
if conanfile.options.get_safe("header_only"):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this if be part of the header_only automation? Maybe not, I guess the meaning of auto-shared_fpic is "you can remove these options if they are not necessary"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think is fine as is but would require a clear explanation in the docs

conanfile.options.rm_safe("fPIC")
conanfile.options.rm_safe("shared")
elif conanfile.options.get_safe("shared"):
conanfile.options.rm_safe("fPIC")


def default_package_id(conanfile):
def auto_header_only_package_id(conanfile):
if conanfile.options.get_safe("header_only") or conanfile.package_type is PackageType.HEADER:
conanfile.info.clear()
7 changes: 4 additions & 3 deletions conans/client/graph/compute_pid.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from conans.errors import conanfile_exception_formatter, ConanInvalidConfiguration, \
conanfile_remove_attr, ConanException
from conans.model.info import ConanInfo, RequirementsInfo, RequirementInfo, PythonRequiresInfo
from conan.tools import default_package_id
from conans.client.conanfile.implementations import auto_header_only_package_id


def compute_package_id(node, new_config):
Expand Down Expand Up @@ -82,6 +82,7 @@ def run_validate_package_id(conanfile):
with conanfile_exception_formatter(conanfile, "package_id"):
with conanfile_remove_attr(conanfile, ['cpp_info', 'settings', 'options'], "package_id"):
conanfile.package_id()
else:
default_package_id(conanfile)
elif "auto_header_only" in conanfile.implements:
auto_header_only_package_id(conanfile)

conanfile.info.validate()
2 changes: 2 additions & 0 deletions conans/model/conan_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ class ConanFile:
default_options = None
package_type = None

implements = []

provides = None
deprecated = None

Expand Down
2 changes: 2 additions & 0 deletions conans/test/functional/toolchains/cmake/test_cmake.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class App(ConanFile):
generators = "CMakeDeps"
options = {"shared": [True, False], "fPIC": [True, False]}
default_options = {"shared": False, "fPIC": True}
implements = ["auto_shared_fpic", "auto_header_only"]
def generate(self):
tc = CMakeToolchain(self)
Expand Down Expand Up @@ -395,6 +396,7 @@ def test_toolchain_linux(self, build_type, cppstd, arch, libcxx, shared):
"CMAKE_EXE_LINKER_FLAGS": arch_str,
"COMPILE_DEFINITIONS": defines,
# fPIC is managed automatically depending on the shared option value
# if implements = ["auto_shared_fpic", "auto_header_only"]
"CMAKE_POSITION_INDEPENDENT_CODE": "ON" if not shared else ""
}

Expand Down
56 changes: 6 additions & 50 deletions conans/test/integration/options/test_configure_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ class ConfigureOptionsTest(unittest.TestCase):
])
def test_methods_not_defined(self, settings_os, shared, fpic, header_only, result):
"""
Test that options are managed automatically when methods config_otpions and configure are not
defined.
Test that options are managed automatically when methods config_options and configure are not
defined and implements = ["auto_shared_fpic", "auto_header_only"].
Check that header only package gets its unique package ID.
"""
client = TestClient()
Expand All @@ -39,6 +39,7 @@ class Pkg(ConanFile):
settings = "os", "compiler", "arch", "build_type"
options = {{"shared": [True, False], "fPIC": [True, False], "header_only": [True, False]}}
default_options = {{"shared": {shared}, "fPIC": {fpic}, "header_only": {header_only}}}
implements = ["auto_shared_fpic", "auto_header_only"]

def build(self):
shared = self.options.get_safe("shared")
Expand Down Expand Up @@ -73,7 +74,7 @@ def build(self):
])
def test_optout(self, settings_os, shared, fpic, header_only, result):
"""
Test that options are not managed automatically when methods are defined.
Test that options are not managed automatically when methods are defined even if implements = ["auto_shared_fpic", "auto_header_only"]
Check that header only package gets its unique package ID.
"""
client = TestClient()
Expand All @@ -84,6 +85,7 @@ class Pkg(ConanFile):
settings = "os", "compiler", "arch", "build_type"
options = {{"shared": [True, False], "fPIC": [True, False], "header_only": [True, False]}}
default_options = {{"shared": {shared}, "fPIC": {fpic}, "header_only": {header_only}}}
implements = ["auto_shared_fpic", "auto_header_only"]

def config_options(self):
pass
Expand All @@ -104,53 +106,6 @@ def build(self):
if header_only:
self.assertIn("Package 'da39a3ee5e6b4b0d3255bfef95601890afd80709' created", client.out)

@parameterized.expand([
["Linux", False, False, False, [False, False, False]],
["Windows", False, False, False, [False, None, False]],
["Windows", True, False, False, [True, None, False]],
["Windows", False, False, True, [None, None, True]],
["Linux", False, False, True, [None, None, True]],
["Linux", True, True, False, [True, None, False]],
["Linux", True, False, False, [True, None, False]],
["Linux", True, True, True, [None, None, True]],
["Linux", True, True, True, [None, None, True]],
["Linux", False, True, False, [False, True, False]],
["Linux", False, True, False, [False, True, False]],
])
def test_methods_defined_explicit(self, settings_os, shared, fpic, header_only, result):
"""
Test that options are managed when the tool is used in methods defined by user.
Check that header only package gets its unique package ID.
"""
client = TestClient()
conanfile = textwrap.dedent(f"""\
from conan import ConanFile
from conan.tools import default_config_options, default_configure

class Pkg(ConanFile):
settings = "os", "compiler", "arch", "build_type"
options = {{"shared": [True, False], "fPIC": [True, False], "header_only": [True, False]}}
default_options = {{"shared": {shared}, "fPIC": {fpic}, "header_only": {header_only}}}

def config_options(self):
default_config_options(self)

def configure(self):
default_configure(self)

def build(self):
shared = self.options.get_safe("shared")
fpic = self.options.get_safe("fPIC")
header_only = self.options.get_safe("header_only")
self.output.info(f"shared: {{shared}}, fPIC: {{fpic}}, header only: {{header_only}}")
""")
client.save({"conanfile.py": conanfile})
client.run(f"create . --name=pkg --version=0.1 -s os={settings_os}")
result = f"shared: {result[0]}, fPIC: {result[1]}, header only: {result[2]}"
self.assertIn(result, client.out)
if header_only:
self.assertIn("Package 'da39a3ee5e6b4b0d3255bfef95601890afd80709' created", client.out)

def test_header_package_type_pid(self):
"""
Test that we get the pid for header only when package type is set to header-library
Expand All @@ -162,6 +117,7 @@ def test_header_package_type_pid(self):
class Pkg(ConanFile):
settings = "os", "compiler", "arch", "build_type"
package_type = "header-library"
implements = ["auto_shared_fpic", "auto_header_only"]

""")
client.save({"conanfile.py": conanfile})
Expand Down
4 changes: 2 additions & 2 deletions conans/test/integration/package_id/test_default_package_id.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ def test_default_package_id_options(typedep, typeconsumer, different_id):
"""
c = TestClient()
dep = GenConanfile("dep", "0.1").with_option("myopt", [True, False]) \
.with_package_type(typedep)
.with_package_type(typedep).with_class_attribute('implements = ["auto_shared_fpic", "auto_header_only"]')
consumer = GenConanfile("consumer", "0.1").with_requires("dep/0.1")\
.with_package_type(typeconsumer)
.with_package_type(typeconsumer).with_class_attribute('implements = ["auto_shared_fpic", "auto_header_only"]')

c.save({"dep/conanfile.py": dep,
"consumer/conanfile.py": consumer})
Expand Down