-
Notifications
You must be signed in to change notification settings - Fork 981
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New cppinfo and CMakeDeps as example of usage (#8766)
* wip * wip * wip * wip * wip * wip * wip * fix * wip * wip * wip * wip * remove unused * wip * wip * wip * wip * wip * minor changes * wip * fix local flow * wip * simplify code * change update function name * generalize name * start build modules support * wip * wip * update test * old behaviour * wip * wip * add custom content to pkg-config generator * add tests with components * add some unit tests * remove translate * revert changes * revert changes * revert changes * use other properties names * update tests * fix filename * not bad * review * fix gets * add tests * fix test * add note * test multi * move to integration * fix name * revert change * add more tests * remove argument * Fix win test * review * review * minor refactor * Review * Removed patterns vars * Fixed tests * change name * Remove import * Test passing * Adadpter CMakeDeps * removed version access * lines * Fixed tests * PEPOCHO * print * Mock * traces for test * more asserts * traces * try host only * Replace path windows * Default get name to old mechanism * review * Renamed var Co-authored-by: czoido <[email protected]> Co-authored-by: James <[email protected]>
- Loading branch information
1 parent
1a16f96
commit b3d5245
Showing
7 changed files
with
581 additions
and
177 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
import copy | ||
from collections import OrderedDict | ||
|
||
from conans.errors import ConanException | ||
from conans.model.build_info import DefaultOrderedDict | ||
|
||
_DIRS_VAR_NAMES = ["includedirs", "srcdirs", "libdirs", "resdirs", "bindirs", "builddirs", | ||
"frameworkdirs"] | ||
_FIELD_VAR_NAMES = ["system_libs", "frameworks", "libs", "defines", "cflags", "cxxflags", | ||
"sharedlinkflags", "exelinkflags"] | ||
|
||
|
||
class _NewComponent(object): | ||
|
||
def __init__(self): | ||
|
||
# ###### PROPERTIES | ||
self._generator_properties = {} | ||
|
||
# ###### DIRECTORIES | ||
self.includedirs = [] # Ordered list of include paths | ||
self.srcdirs = [] # Ordered list of source paths | ||
self.libdirs = [] # Directories to find libraries | ||
self.resdirs = [] # Directories to find resources, data, etc | ||
self.bindirs = [] # Directories to find executables and shared libs | ||
self.builddirs = [] | ||
self.frameworkdirs = [] | ||
|
||
# ##### FIELDS | ||
self.system_libs = [] # Ordered list of system libraries | ||
self.frameworks = [] # Macos .framework | ||
self.libs = [] # The libs to link against | ||
self.defines = [] # preprocessor definitions | ||
self.cflags = [] # pure C flags | ||
self.cxxflags = [] # C++ compilation flags | ||
self.sharedlinkflags = [] # linker flags | ||
self.exelinkflags = [] # linker flags | ||
|
||
self.sysroot = "" | ||
self.requires = [] | ||
|
||
@property | ||
def required_component_names(self): | ||
""" Names of the required components of the same package (not scoped with ::)""" | ||
return [r for r in self.requires if "::" not in r] | ||
|
||
def set_property(self, property_name, value, generator=None): | ||
self._generator_properties.setdefault(generator, {})[property_name] = value | ||
|
||
def get_property(self, property_name, generator=None): | ||
if generator: | ||
try: | ||
return self._generator_properties[generator][property_name] | ||
except KeyError: | ||
pass | ||
try: | ||
return self._generator_properties[None][property_name] | ||
except KeyError: | ||
pass | ||
|
||
|
||
class NewCppInfo(object): | ||
|
||
def __init__(self): | ||
super(NewCppInfo, self).__init__() | ||
self.components = DefaultOrderedDict(lambda: _NewComponent()) | ||
self.components[None] = _NewComponent() # Main package is a component with None key | ||
|
||
def __getattr__(self, attr): | ||
return getattr(self.components[None], attr) | ||
|
||
def __setattr__(self, attr, value): | ||
if attr in ["components"]: | ||
super(NewCppInfo, self).__setattr__(attr, value) | ||
else: | ||
setattr(self.components[None], attr, value) | ||
|
||
@property | ||
def has_components(self): | ||
return len(self.components) > 1 | ||
|
||
@staticmethod | ||
def from_old_cppinfo(old): | ||
ret = NewCppInfo() | ||
for varname in _DIRS_VAR_NAMES + _FIELD_VAR_NAMES: | ||
setattr(ret, varname, getattr(old, varname)) | ||
|
||
ret._generator_properties = copy.copy(old._generator_properties) | ||
|
||
# COMPONENTS | ||
for cname, c in old.components.items(): | ||
for varname in _DIRS_VAR_NAMES + _FIELD_VAR_NAMES: | ||
setattr(ret.components[cname], varname, getattr(c, varname)) | ||
ret.components[cname].requires = copy.copy(c.requires) | ||
ret.components[cname]._generator_properties = copy.copy(c._generator_properties) | ||
return ret | ||
|
||
def get_sorted_components(self): | ||
"""Order the components taking into account if they depend on another component in the | ||
same package (not scoped with ::). First less dependant | ||
return: {component_name: component} | ||
""" | ||
processed = [] # Names of the components ordered | ||
# FIXME: Cache the sort | ||
while (len(self.components) - 1) > len(processed): | ||
for name, c in self.components.items(): | ||
if name is None: | ||
continue | ||
req_processed = [n for n in c.required_component_names if n not in processed] | ||
if not req_processed and name not in processed: | ||
processed.append(name) | ||
|
||
return OrderedDict([(cname, self.components[cname]) for cname in processed]) | ||
|
||
def aggregate_components(self): | ||
"""Aggregates all the components as global values""" | ||
if self.has_components: | ||
components = self.get_sorted_components() | ||
cnames = list(components.keys()) | ||
cnames.reverse() # More dependant first | ||
|
||
# Clean global values | ||
for n in _DIRS_VAR_NAMES + _FIELD_VAR_NAMES: | ||
setattr(self.components[None], n, []) | ||
|
||
for name in cnames: | ||
component = components[name] | ||
for n in _DIRS_VAR_NAMES + _FIELD_VAR_NAMES: | ||
dest = getattr(self.components[None], n) | ||
dest += [i for i in getattr(component, n) if i not in dest] | ||
self.components[None].requires.extend(component.requires) | ||
# The generator properties are not aggregated, should be defined in the root | ||
# cpp info if needed | ||
# FIXME: What to do about sysroot? | ||
# Leave only the aggregated value | ||
main_value = self.components[None] | ||
self.components = DefaultOrderedDict(lambda: _NewComponent()) | ||
self.components[None] = main_value | ||
|
||
def merge(self, other): | ||
# TODO: Still not used, to be used by global generators | ||
# If we are merging isolated cppinfo objects is because the generator is "global" | ||
# (dirs and flags in link order in a single list) so first call | ||
# cpp_info.aggregate_components() | ||
if self.has_components or other.has_components: | ||
raise ConanException("Cannot aggregate two cppinfo objects with components. " | ||
"Do cpp_info.aggregate_components() first") | ||
for n in _DIRS_VAR_NAMES + _FIELD_VAR_NAMES: | ||
dest = getattr(self.components[None], n) | ||
dest += [i for i in getattr(other, n) if i not in dest] | ||
|
||
def copy(self): | ||
ret = NewCppInfo() | ||
ret._generator_properties = copy.copy(self._generator_properties) | ||
ret.components = DefaultOrderedDict(lambda: _NewComponent()) | ||
for comp_name in self.components: | ||
ret.components[comp_name] = copy.copy(self.components[comp_name]) | ||
return ret | ||
|
||
@property | ||
def required_components(self): | ||
"""Returns a list of tuples with (require, component_name) required by the package | ||
If the require is internal (to another component), the require will be None""" | ||
# FIXME: Cache the value | ||
ret = [] | ||
for comp in self.components.values(): | ||
ret.extend([r.split("::") for r in comp.requires if "::" in r and r not in ret]) | ||
ret.extend([(None, r) for r in comp.requires if "::" not in r and r not in ret]) | ||
return ret |
163 changes: 163 additions & 0 deletions
163
conans/test/functional/conan_build_info/new_build_info_test.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
import pytest | ||
|
||
from conans.errors import ConanException | ||
from conans.model.build_info import CppInfo | ||
from conans.model.new_build_info import NewCppInfo, _DIRS_VAR_NAMES, _FIELD_VAR_NAMES | ||
|
||
|
||
def test_components_order(): | ||
cppinfo = NewCppInfo() | ||
cppinfo.components["c1"].requires = ["c4", "OtherPackage::OtherComponent2"] | ||
cppinfo.components["c2"].requires = ["OtherPackage::OtherComponent"] | ||
cppinfo.components["c3"].requires = ["c2"] | ||
cppinfo.components["c4"].requires = ["c3"] | ||
sorted_c = list(cppinfo.get_sorted_components().keys()) | ||
assert sorted_c == ["c2", "c3", "c4", "c1"] | ||
|
||
|
||
def test_generator_properties_copy(): | ||
cppinfo = NewCppInfo() | ||
cppinfo.set_property("foo", "foo_value", "generator1") | ||
cppinfo.set_property("foo", "var_value", "generator2") | ||
cppinfo.set_property("foo2", "foo2_value", "generator1") | ||
|
||
copied = cppinfo.copy() | ||
|
||
assert copied.get_property("foo") is None | ||
assert copied.get_property("foo", "generator1") == "foo_value" | ||
assert copied.get_property("foo", "generator2") == "var_value" | ||
|
||
|
||
def test_component_aggregation(): | ||
cppinfo = NewCppInfo() | ||
|
||
cppinfo.includedirs = ["includedir"] | ||
cppinfo.libdirs = ["libdir"] | ||
cppinfo.srcdirs = ["srcdir"] | ||
cppinfo.bindirs = ["bindir"] | ||
cppinfo.builddirs = ["builddir"] | ||
cppinfo.frameworkdirs = ["frameworkdir"] | ||
|
||
cppinfo.components["c2"].includedirs = ["includedir_c2"] | ||
cppinfo.components["c2"].libdirs = ["libdir_c2"] | ||
cppinfo.components["c2"].srcdirs = ["srcdir_c2"] | ||
cppinfo.components["c2"].bindirs = ["bindir_c2"] | ||
cppinfo.components["c2"].builddirs = ["builddir_c2"] | ||
cppinfo.components["c2"].frameworkdirs = ["frameworkdir_c2"] | ||
cppinfo.components["c2"].cxxflags = ["cxxflags_c2"] | ||
cppinfo.components["c2"].defines = ["defines_c2"] | ||
|
||
cppinfo.components["c1"].requires = ["c2", "LIB_A::C1"] | ||
cppinfo.components["c1"].includedirs = ["includedir_c1"] | ||
cppinfo.components["c1"].libdirs = ["libdir_c1"] | ||
cppinfo.components["c1"].srcdirs = ["srcdir_c1"] | ||
cppinfo.components["c1"].bindirs = ["bindir_c1"] | ||
cppinfo.components["c1"].builddirs = ["builddir_c1"] | ||
cppinfo.components["c1"].frameworkdirs = ["frameworkdir_c1"] | ||
cppinfo.components["c1"].cxxflags = ["cxxflags_c1"] | ||
cppinfo.components["c1"].defines = ["defines_c1"] | ||
|
||
ret = cppinfo.copy() | ||
ret.aggregate_components() | ||
|
||
assert ret.includedirs == ["includedir_c1", "includedir_c2"] | ||
assert ret.libdirs == ["libdir_c1", "libdir_c2"] | ||
assert ret.srcdirs == ["srcdir_c1", "srcdir_c2"] | ||
assert ret.bindirs == ["bindir_c1", "bindir_c2"] | ||
assert ret.builddirs == ["builddir_c1", "builddir_c2"] | ||
assert ret.frameworkdirs == ["frameworkdir_c1", "frameworkdir_c2"] | ||
assert ret.cxxflags == ["cxxflags_c1", "cxxflags_c2"] | ||
assert ret.defines == ["defines_c1", "defines_c2"] | ||
|
||
# If we change the internal graph the order is different | ||
cppinfo.components["c1"].requires = [] | ||
cppinfo.components["c2"].requires = ["c1"] | ||
|
||
ret = cppinfo.copy() | ||
ret.aggregate_components() | ||
|
||
assert ret.includedirs == ["includedir_c2", "includedir_c1"] | ||
assert ret.libdirs == ["libdir_c2", "libdir_c1"] | ||
assert ret.srcdirs == ["srcdir_c2", "srcdir_c1"] | ||
assert ret.bindirs == ["bindir_c2", "bindir_c1"] | ||
assert ret.builddirs == ["builddir_c2", "builddir_c1"] | ||
assert ret.frameworkdirs == ["frameworkdir_c2", "frameworkdir_c1"] | ||
|
||
|
||
def norm(paths): | ||
return [d.replace("\\", "/") for d in paths] | ||
|
||
|
||
def test_cpp_info_merge_with_components(): | ||
"""If we try to merge a cpp info with another one and some of them have components, assert""" | ||
cppinfo = NewCppInfo() | ||
cppinfo.components["foo"].cxxflags = ["var"] | ||
|
||
other = NewCppInfo() | ||
other.components["foo2"].cxxflags = ["var2"] | ||
|
||
with pytest.raises(ConanException) as exc: | ||
cppinfo.merge(other) | ||
|
||
assert "Cannot aggregate two cppinfo objects with components" in str(exc.value) | ||
|
||
|
||
def test_cpp_info_merge_aggregating_components_first(): | ||
cppinfo = NewCppInfo() | ||
for n in _DIRS_VAR_NAMES + _FIELD_VAR_NAMES: | ||
setattr(cppinfo.components["foo"], n, ["var_{}_1".format(n), "var_{}_2".format(n)]) | ||
setattr(cppinfo.components["foo2"], n, ["var2_{}_1".format(n), "var2_{}_2".format(n)]) | ||
|
||
cppinfo.components["foo"].requires = ["foo2"] # Deterministic order | ||
|
||
other = NewCppInfo() | ||
for n in _DIRS_VAR_NAMES + _FIELD_VAR_NAMES: | ||
setattr(other.components["boo"], n, ["jar_{}_1".format(n), "jar_{}_2".format(n)]) | ||
setattr(other.components["boo2"], n, ["jar2_{}_1".format(n), "jar2_{}_2".format(n)]) | ||
|
||
other.components["boo"].requires = ["boo2"] # Deterministic order | ||
|
||
cppinfo.aggregate_components() | ||
other.aggregate_components() | ||
|
||
cppinfo.merge(other) | ||
|
||
for n in _DIRS_VAR_NAMES + _FIELD_VAR_NAMES: | ||
assert getattr(cppinfo, n) == ["var_{}_1".format(n), "var_{}_2".format(n), | ||
"var2_{}_1".format(n), "var2_{}_2".format(n), | ||
"jar_{}_1".format(n), "jar_{}_2".format(n), | ||
"jar2_{}_1".format(n), "jar2_{}_2".format(n)] | ||
|
||
|
||
def test_from_old_cppinfo_components(): | ||
oldcppinfo = CppInfo("ref", "/root/") | ||
for n in _DIRS_VAR_NAMES + _FIELD_VAR_NAMES: | ||
setattr(oldcppinfo.components["foo"], n, ["var_{}_1".format(n), "var_{}_2".format(n)]) | ||
setattr(oldcppinfo.components["foo2"], n, ["var2_{}_1".format(n), "var2_{}_2".format(n)]) | ||
|
||
# The names and filenames are not copied to the new model | ||
oldcppinfo.components["foo"].names["Gen"] = ["MyName"] | ||
oldcppinfo.filenames["Gen"] = ["Myfilename"] | ||
|
||
cppinfo = NewCppInfo.from_old_cppinfo(oldcppinfo) | ||
|
||
assert isinstance(cppinfo, NewCppInfo) | ||
|
||
for n in _DIRS_VAR_NAMES + _FIELD_VAR_NAMES: | ||
assert getattr(cppinfo.components["foo"], n) == ["var_{}_1".format(n), | ||
"var_{}_2".format(n)] | ||
assert getattr(cppinfo.components["foo2"], n) == ["var2_{}_1".format(n), | ||
"var2_{}_2".format(n)] | ||
|
||
|
||
def test_from_old_cppinfo_no_components(): | ||
oldcppinfo = CppInfo("ref", "/root/") | ||
for n in _DIRS_VAR_NAMES + _FIELD_VAR_NAMES: | ||
setattr(oldcppinfo, n, ["var_{}_1".format(n), "var_{}_2".format(n)]) | ||
|
||
cppinfo = NewCppInfo.from_old_cppinfo(oldcppinfo) | ||
|
||
assert isinstance(cppinfo, NewCppInfo) | ||
|
||
for n in _DIRS_VAR_NAMES + _FIELD_VAR_NAMES: | ||
assert getattr(cppinfo, n) == ["var_{}_1".format(n), "var_{}_2".format(n)] |
Oops, something went wrong.