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

topological order of buildenv #9491

Merged
merged 3 commits into from Aug 30, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 5 additions & 3 deletions conan/tools/env/virtualbuildenv.py
Expand Up @@ -22,8 +22,9 @@ def environment(self):
profile_env = self._conanfile.buildenv
build_env.compose_env(profile_env)

for require, build_require in self._conanfile.dependencies.build.items():
if require.direct:
build_requires = self._conanfile.dependencies.build.topological_sort
for require, build_require in reversed(build_requires.items()):
if require.direct: # Only buildenv_info from direct deps is propagated
# higher priority, explicit buildenv_info
if build_require.buildenv_info:
build_env.compose_env(build_require.buildenv_info)
Expand All @@ -34,7 +35,8 @@ def environment(self):
build_env.compose_env(runenv_from_cpp_info(self._conanfile, build_require.cpp_info))

# Requires in host context can also bring some direct buildenv_info
for require in self._conanfile.dependencies.host.values():
host_requires = self._conanfile.dependencies.host.topological_sort
for require in reversed(host_requires.values()):
if require.buildenv_info:
build_env.compose_env(require.buildenv_info)

Expand Down
3 changes: 3 additions & 0 deletions conans/model/conanfile_interface.py
Expand Up @@ -19,6 +19,9 @@ def __eq__(self, other):
"""
return self._conanfile == other._conanfile

def __hash__(self):
return hash(self._conanfile)

def __ne__(self, other):
return not self.__eq__(other)

Expand Down
30 changes: 28 additions & 2 deletions conans/model/dependencies.py
@@ -1,6 +1,5 @@
from collections import OrderedDict

from conans.client.graph.graph import CONTEXT_BUILD
from conans.model.conanfile_interface import ConanFileInterface
from conans.model.ref import ConanFileReference

Expand Down Expand Up @@ -122,7 +121,34 @@ def expand(nodes, is_build, is_test):
return ConanFileDependencies(d)

def filter(self, require_filter):
return super(ConanFileDependencies, self).filter(require_filter)
# FIXME: Copy of hte above, to return ConanFileDependencies class object
def filter_fn(require):
for k, v in require_filter.items():
if getattr(require, k) != v:
return False
return True

data = OrderedDict((k, v) for k, v in self._data.items() if filter_fn(k))
return ConanFileDependencies(data, require_filter)

@property
def topological_sort(self):
# Return first independent nodes, final ones are the more direct deps
result = OrderedDict()
opened = self._data.copy()

while opened:
opened_values = set(opened.values())
new_opened = OrderedDict()
for req, conanfile in opened.items():
deps_in_opened = any(d in opened_values for d in conanfile.dependencies.values())
if deps_in_opened:
new_opened[req] = conanfile # keep it for next iteration
else:
result[req] = conanfile # No dependencies in open set!

opened = new_opened
return ConanFileDependencies(result)

@property
def direct_host(self):
Expand Down
54 changes: 54 additions & 0 deletions conans/test/integration/build_requires/build_requires_test.py
Expand Up @@ -451,3 +451,57 @@ def build(self):
.with_build_requires("harfbuzz/1.0@test/test")})
client.run("install . --build=missing")
self.assertIn("ZLIBS LIBS: ['myzlib']", client.out)


def test_dependents_new_buildenv():
client = TestClient()
boost = textwrap.dedent("""
from conans import ConanFile
class Boost(ConanFile):
def package_info(self):
self.buildenv_info.define_path("PATH", "myboostpath")
""")
other = textwrap.dedent("""
from conans import ConanFile
class Other(ConanFile):
requires = "boost/1.0"
def package_info(self):
self.buildenv_info.append_path("PATH", "myotherpath")
memsharded marked this conversation as resolved.
Show resolved Hide resolved
self.buildenv_info.prepend_path("PATH", "myotherprepend")
""")
consumer = textwrap.dedent("""
from conans import ConanFile
from conan.tools.env import VirtualBuildEnv
import os
class Lib(ConanFile):
build_requires = {}
def generate(self):
build_env = VirtualBuildEnv(self).environment()
with build_env.apply():
self.output.info("LIB PATH %s" % os.getenv("PATH"))
""")
client.save({"boost/conanfile.py": boost,
"other/conanfile.py": other,
"consumer/conanfile.py": consumer.format('"boost/1.0", "other/1.0"'),
"profile_define": "[buildenv]\nPATH=(path)profilepath",
"profile_append": "[buildenv]\nPATH+=(path)profilepath",
"profile_prepend": "[buildenv]\nPATH=+(path)profilepath"})
client.run("create boost boost/1.0@")
client.run("create other other/1.0@")
client.run("install consumer")
result = os.pathsep.join(["myotherprepend", "myboostpath", "myotherpath"])
assert "LIB PATH {}".format(result) in client.out

# Now test if we declare in different order, still topological order should be respected
client.save({"consumer/conanfile.py": consumer.format('"other/1.0", "boost/1.0"')})
client.run("install consumer")
assert "LIB PATH {}".format(result) in client.out

client.run("install consumer -pr=profile_define")
assert "LIB PATH profilepath" in client.out
client.run("install consumer -pr=profile_append")
result = os.pathsep.join(["myotherprepend", "myboostpath", "myotherpath", "profilepath"])
assert "LIB PATH {}".format(result) in client.out
client.run("install consumer -pr=profile_prepend")
result = os.pathsep.join(["profilepath", "myotherprepend", "myboostpath", "myotherpath"])
assert "LIB PATH {}".format(result) in client.out
4 changes: 2 additions & 2 deletions conans/test/integration/environment/test_env.py
Expand Up @@ -267,13 +267,13 @@ def generate(self):
""")
client.save({"conanfile.py": consumer}, clean_first=True)
client.run("install . -s:b os=Windows -s:h os=Linux --build")
assert "BUILDENV: MyOpenSSLWindowsValue MyGCCValue "\
assert "BUILDENV: MyGCCValue MyOpenSSLWindowsValue "\
"MyCMakeRunValue MyCMakeBuildValue!!!" in client.out
assert "RUNENV: MyOpenSSLLinuxValue!!!" in client.out

# Even if the generator is duplicated in command line (it used to fail due to bugs)
client.run("install . -s:b os=Windows -s:h os=Linux --build -g VirtualRunEnv -g VirtualBuildEnv")
assert "BUILDENV: MyOpenSSLWindowsValue MyGCCValue "\
assert "BUILDENV: MyGCCValue MyOpenSSLWindowsValue "\
"MyCMakeRunValue MyCMakeBuildValue!!!" in client.out
assert "RUNENV: MyOpenSSLLinuxValue!!!" in client.out

Expand Down