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

make CppInfo an importable tool #14101

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
8 changes: 8 additions & 0 deletions conan/tools/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from conans.model.build_info import CppInfo as _CppInfo


def CppInfo(conanfile):
# Creation of a CppInfo object, to decouple the creation from the actual internal location
# that at the moment doesn't require a ``conanfile`` argument, but might require in the future
# and allow us to refactor the location of conans.model.build_info import CppInfo
return _CppInfo()
4 changes: 2 additions & 2 deletions conan/tools/gnu/autotoolsdeps.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from conan.internal import check_duplicated_generator
from conan.tools import CppInfo
from conan.tools.env import Environment
from conan.tools.gnu.gnudeps_flags import GnuDepsFlags
from conans.model.build_info import CppInfo


class AutotoolsDeps:
Expand All @@ -18,7 +18,7 @@ def ordered_deps(self):
return self._ordered_deps

def _get_cpp_info(self):
ret = CppInfo()
ret = CppInfo(self._conanfile)
for dep in self.ordered_deps:
dep_cppinfo = dep.cpp_info.aggregated_components()
# In case we have components, aggregate them, we do not support isolated
Expand Down
4 changes: 2 additions & 2 deletions conan/tools/microsoft/nmakedeps.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import os

from conan.internal import check_duplicated_generator
from conan.tools import CppInfo
from conan.tools.env import Environment
from conans.model.build_info import CppInfo


class NMakeDeps(object):
Expand All @@ -16,7 +16,7 @@ def __init__(self, conanfile):

# TODO: This is similar from AutotoolsDeps: Refactor and make common
def _get_cpp_info(self):
ret = CppInfo()
ret = CppInfo(self._conanfile)
deps = self._conanfile.dependencies.host.topological_sort
deps = [dep for dep in reversed(deps.values())]
for dep in deps:
Expand Down
44 changes: 44 additions & 0 deletions conans/model/build_info.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import copy
import json
import os
from collections import OrderedDict, defaultdict

from conan.api.output import ConanOutput
from conans.errors import ConanException
from conans.util.files import load, save

_DIRS_VAR_NAMES = ["_includedirs", "_srcdirs", "_libdirs", "_resdirs", "_bindirs", "_builddirs",
"_frameworkdirs", "_objects"]
Expand Down Expand Up @@ -112,6 +114,35 @@ def serialize(self):
"properties": self._properties
}

@staticmethod
def deserialize(contents):
result = _Component()
# TODO: Refactor to avoid this repetition
fields = [
"includedirs",
"srcdirs",
"libdirs",
"resdirs",
"bindirs",
"builddirs",
"frameworkdirs",
"system_libs",
"frameworks",
"libs",
"defines",
"cflags",
"cxxflags",
"sharedlinkflags",
"exelinkflags",
"objects",
"sysroot",
"requires",
"properties"
]
for f in fields:
setattr(result, f"_{f}", contents[f])
return result

@property
def includedirs(self):
if self._includedirs is None:
Expand Down Expand Up @@ -420,6 +451,19 @@ def serialize(self):
ret[component_name] = info.serialize()
return ret

def deserialize(self, content):
self._package = _Component.deserialize(content.pop("root"))
for component_name, info in content.items():
self.components[component_name] = _Component.deserialize(info)
return self

def save(self, path):
save(path, json.dumps(self.serialize()))

def load(self, path):
content = json.loads(load(path))
return self.deserialize(content)

@property
def has_components(self):
return len(self.components) > 0
Expand Down
40 changes: 40 additions & 0 deletions conans/test/integration/conanfile/test_cpp_info_serialize.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import json
import textwrap

from conans.test.utils.tools import TestClient


def test_cpp_info_serialize_round_trip():
""" test that serialize and deserialize CppInfo works
"""
# TODO: Define standard name for file
c = TestClient()
conanfile = textwrap.dedent("""\
import os
from conan import ConanFile
from conan.tools import CppInfo

class Pkg(ConanFile):
name = "pkg"
version = "0.1"

def package(self):
cpp_info = CppInfo(self)
cpp_info.includedirs = ["myinc"]
cpp_info.libs = ["mylib", "myother"]
cpp_info.libdirs = ["mylibs"]
p = os.path.join(self.package_folder, "cpp_info.json")
cpp_info.save(p)

def package_info(self):
cpp_info = CppInfo(self).load("cpp_info.json")
Copy link
Member Author

Choose a reason for hiding this comment

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

This test shows that the interface cannot be a class/static method like CppInfo.load() as we typically do in the codebase, but it is loading on an already existing object (overwriting it)

self.cpp_info = cpp_info
""")

c.save({"conanfile.py": conanfile})
c.run("create . --format=json")
graph = json.loads(c.stdout)
cpp_info = graph["graph"]["nodes"]["1"]["cpp_info"]["root"]
assert cpp_info["includedirs"][0].endswith("myinc")
assert cpp_info["libdirs"][0].endswith("mylibs")
assert cpp_info["libs"] == ["mylib", "myother"]