Skip to content

Commit 1eda2e4

Browse files
authored
Fix/editable layout envconf priority (#19268)
* self.layouts.build.runenv_info/conf_info * fix layout() precedence for build/runenv_info and conf_info
1 parent a9e1bea commit 1eda2e4

File tree

2 files changed

+116
-12
lines changed

2 files changed

+116
-12
lines changed

conan/internal/graph/installer.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -447,12 +447,20 @@ def _call_package_info(self, conanfile, package_folder, is_editable):
447447

448448
# Paste the editable cpp_info but prioritizing it, only if a
449449
# variable is not declared at build/source, the package will keep the value
450-
conanfile.buildenv_info.compose_env(conanfile.layouts.source.buildenv_info)
451-
conanfile.buildenv_info.compose_env(conanfile.layouts.build.buildenv_info)
452-
conanfile.runenv_info.compose_env(conanfile.layouts.source.runenv_info)
453-
conanfile.runenv_info.compose_env(conanfile.layouts.build.runenv_info)
454-
conanfile.conf_info.compose_conf(conanfile.layouts.source.conf_info)
455-
conanfile.conf_info.compose_conf(conanfile.layouts.build.conf_info)
450+
full_buildenv_info = conanfile.layouts.source.buildenv_info.copy()
451+
full_buildenv_info.compose_env(conanfile.layouts.build.buildenv_info)
452+
full_buildenv_info.compose_env(conanfile.buildenv_info)
453+
conanfile.buildenv_info = full_buildenv_info
454+
455+
full_runenv_info = conanfile.layouts.source.runenv_info.copy()
456+
full_runenv_info.compose_env(conanfile.layouts.build.runenv_info)
457+
full_runenv_info.compose_env(conanfile.runenv_info)
458+
conanfile.runenv_info = full_runenv_info
459+
460+
full_conf_info = conanfile.layouts.source.conf_info.copy()
461+
full_conf_info.compose_conf(conanfile.layouts.build.conf_info)
462+
full_conf_info.compose_conf(conanfile.conf_info)
463+
conanfile.conf_info = full_conf_info
456464
else:
457465
conanfile.layouts.package.set_relative_base_folder(conanfile.package_folder)
458466
conanfile.buildenv_info.compose_env(conanfile.layouts.package.buildenv_info)

test/integration/editable/test_editable_envvars.py

Lines changed: 102 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,7 @@ def package_info(self):
2727
""")
2828

2929
c.save({"dep/conanfile.py": dep,
30-
"pkg/conanfile.py": GenConanfile().with_settings("os")
31-
.with_requires("dep/1.0")
32-
.with_generator("VirtualBuildEnv")
33-
.with_generator("VirtualRunEnv")})
30+
"pkg/conanfile.py": GenConanfile().with_settings("os").with_requires("dep/1.0")})
3431
c.run("editable add dep --name=dep --version=1.0")
3532
c.run("install pkg -s os=Linux -s:b os=Linux")
3633
build_path = os.path.join(c.current_folder, "dep", "mybuild", "mylocalbuild")
@@ -56,6 +53,103 @@ def package_info(self):
5653
assert "mylocalsrc" not in runenv
5754

5855

56+
def test_editable_envvars_package_info():
57+
""" if the ``layout()`` defines cpp.build.runenv_info/conf_info and the ``package_info()``
58+
also defines them, the ``package_info()`` values have precedence. This is not symmetrical
59+
with ``cpp_info.includedirs/xxxdirs``. The recommended solution is to use
60+
``self.layouts.package.runenvinfo/conf_info`` instead.
61+
"""
62+
c = TestClient()
63+
dep = textwrap.dedent("""
64+
from conan import ConanFile
65+
class Dep(ConanFile):
66+
name = "dep"
67+
version = "1.0"
68+
def layout(self):
69+
self.layouts.build.runenv_info.define("SOME_PATH", "mypath_layout")
70+
self.cpp.build.includedirs = ["mylayoutinclude"]
71+
self.layouts.build.conf_info.define("user:myconf", "mylayoutconf")
72+
73+
def package_info(self):
74+
print("Running package_info!!")
75+
self.runenv_info.define("SOME_PATH", "mypath_pkginfo")
76+
self.cpp_info.includedirs = ["mypkginfoinclude"]
77+
self.conf_info.define("user:myconf", "mypkginfoconf")
78+
""")
79+
pkg = textwrap.dedent("""
80+
from conan import ConanFile
81+
class Dep(ConanFile):
82+
name = "pkg"
83+
version = "1.0"
84+
settings = "os", "build_type"
85+
requires = "dep/1.0"
86+
def generate(self):
87+
myconf = self.dependencies["dep"].conf_info.get("user:myconf")
88+
self.output.info(f"DEP CONFINFO {myconf}")
89+
""")
90+
91+
c.save({"dep/conanfile.py": dep,
92+
"pkg/conanfile.py": pkg})
93+
c.run("editable add dep ")
94+
c.run("install pkg -s os=Linux -s:b os=Linux -g CMakeDeps")
95+
assert "conanfile.py (pkg/1.0): DEP CONFINFO mylayoutconf" in c.out
96+
cmake = c.load("pkg/dep-release-data.cmake")
97+
assert 'set(dep_INCLUDE_DIRS_RELEASE "${dep_PACKAGE_FOLDER_RELEASE}/mylayoutinclude")' in cmake
98+
runenv = c.load("pkg/conanrunenv-release.sh")
99+
assert f'export SOME_PATH="mypath_layout"' in runenv
100+
101+
102+
def test_editable_envvars_package():
103+
""" This test shows that ``self.layouts.package.runenvinfo/conf_info`` works, ignored
104+
while in editable, but used when regular package
105+
"""
106+
c = TestClient()
107+
dep = textwrap.dedent("""
108+
from conan import ConanFile
109+
class Dep(ConanFile):
110+
name = "dep"
111+
version = "1.0"
112+
def layout(self):
113+
self.layouts.build.runenv_info.define("SOME_VALUE", "mypath_layout")
114+
self.cpp.build.includedirs = ["mylayoutinclude"]
115+
self.layouts.build.conf_info.define("user:myconf", "mylayoutconf")
116+
117+
self.layouts.package.runenv_info.define("SOME_VALUE", "mypath_pkginfo")
118+
self.cpp.package.includedirs = ["mypkginfoinclude"]
119+
self.layouts.package.conf_info.define("user:myconf", "mypkginfoconf")
120+
""")
121+
pkg = textwrap.dedent("""
122+
from conan import ConanFile
123+
class Dep(ConanFile):
124+
name = "pkg"
125+
version = "1.0"
126+
settings = "os", "build_type"
127+
requires = "dep/1.0"
128+
def generate(self):
129+
myconf = self.dependencies["dep"].conf_info.get("user:myconf")
130+
self.output.info(f"DEP CONFINFO {myconf}")
131+
""")
132+
133+
c.save({"dep/conanfile.py": dep,
134+
"pkg/conanfile.py": pkg})
135+
c.run("editable add dep ")
136+
c.run("install pkg -s os=Linux -s:b os=Linux -g CMakeDeps")
137+
assert "conanfile.py (pkg/1.0): DEP CONFINFO mylayoutconf" in c.out
138+
cmake = c.load("pkg/dep-release-data.cmake")
139+
assert 'set(dep_INCLUDE_DIRS_RELEASE "${dep_PACKAGE_FOLDER_RELEASE}/mylayoutinclude")' in cmake
140+
runenv = c.load("pkg/conanrunenv-release.sh")
141+
assert f'export SOME_VALUE="mypath_layout"' in runenv
142+
143+
c.run("editable remove dep")
144+
c.run("create dep")
145+
c.run("install pkg -s os=Linux -s:b os=Linux -g CMakeDeps")
146+
assert "conanfile.py (pkg/1.0): DEP CONFINFO mypkginfoconf" in c.out
147+
cmake = c.load("pkg/dep-release-data.cmake")
148+
assert 'set(dep_INCLUDE_DIRS_RELEASE "${dep_PACKAGE_FOLDER_RELEASE}/mypkginfoinclude")' in cmake
149+
runenv = c.load("pkg/conanrunenv-release.sh")
150+
assert f'export SOME_VALUE="mypath_pkginfo"' in runenv
151+
152+
59153
def test_editable_conf():
60154
c = TestClient()
61155
# TODO: Define if we want conf.xxxx_path(), instead of (..., path=True) methods
@@ -67,7 +161,8 @@ def layout(self):
67161
self.folders.build = "mybuild"
68162
self.layouts.source.conf_info.append_path("user:myconf", "mylocalsrc")
69163
self.layouts.build.conf_info.append_path("user:myconf", "mylocalbuild")
70-
self.layouts.build.conf_info.update_path("user:mydictconf", {"a": "mypatha", "b": "mypathb"})
164+
self.layouts.build.conf_info.update_path("user:mydictconf", {"a": "mypatha",
165+
"b": "mypathb"})
71166
self.layouts.build.conf_info.define_path("user:mydictconf2", {"c": "mypathc"})
72167
""")
73168

@@ -80,7 +175,8 @@ def generate(self):
80175
self.output.info(f"CONF: {conf}")
81176
dictconf = self.dependencies["dep"].conf_info.get("user:mydictconf", check_type=dict)
82177
self.output.info(f"CONFDICT: {dictconf}")
83-
dictconf2 = self.dependencies["dep"].conf_info.get("user:mydictconf2", check_type=dict)
178+
dictconf2 = self.dependencies["dep"].conf_info.get("user:mydictconf2",
179+
check_type=dict)
84180
self.output.info(f"CONFDICT: {dictconf2}")
85181
""")
86182
c.save({"dep/conanfile.py": dep,

0 commit comments

Comments
 (0)