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

[incubating][CMakeDeps] Adding more CMake paths #17668

Merged
Merged
Show file tree
Hide file tree
Changes from 2 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
80 changes: 48 additions & 32 deletions conan/tools/cmake/cmakedeps2/cmakedeps.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,30 +165,55 @@ def __init__(self, cmakedeps, conanfile):
self._conanfile = conanfile
self._cmakedeps = cmakedeps

def _get_cmake_paths(self, requirements, dirs_name):
paths = {}
cmake_vars = {
"bindirs": "CMAKE_PROGRAM_PATH",
"libdirs": "CMAKE_LIBRARY_PATH",
"includedirs": "CMAKE_INCLUDE_PATH",
}
for req, dep in requirements:
cppinfo = dep.cpp_info.aggregated_components()
cppinfo_dirs = getattr(cppinfo, dirs_name, [])
if not cppinfo_dirs:
continue
previous = paths.get(req.ref.name)
if previous:
self._conanfile.output.info(f"There is already a '{req.ref}' package contributing"
f" to {cmake_vars[dirs_name]}. Using the one"
f" defined by the context={dep.context}.")
paths[req.ref.name] = cppinfo_dirs
return [d for dirs in paths.values() for d in dirs]

def generate(self):
template = textwrap.dedent("""\
{% for pkg_name, folder in pkg_paths.items() %}
set({{pkg_name}}_DIR "{{folder}}")
{% endfor %}
{% if host_runtime_dirs %}
set(CONAN_RUNTIME_LIB_DIRS {{ host_runtime_dirs }} )
# Only for VS, needs CMake>=3.27
set(CMAKE_VS_DEBUGGER_ENVIRONMENT "PATH=${CONAN_RUNTIME_LIB_DIRS};%PATH%")
{% endif %}
{% if cmake_program_path %}
list(PREPEND CMAKE_PROGRAM_PATH {{ cmake_program_path }})
{% endif %}
""")

{% for pkg_name, folder in pkg_paths.items() %}
set({{pkg_name}}_DIR "{{folder}}")
{% endfor %}
{% if host_runtime_dirs %}
set(CONAN_RUNTIME_LIB_DIRS {{ host_runtime_dirs }} )
# Only for VS, needs CMake>=3.27
set(CMAKE_VS_DEBUGGER_ENVIRONMENT "PATH=${CONAN_RUNTIME_LIB_DIRS};%PATH%")
{% endif %}
{% if cmake_program_path %}
list(PREPEND CMAKE_PROGRAM_PATH {{ cmake_program_path }})
{% endif %}
{% if cmake_library_path %}
list(PREPEND CMAKE_LIBRARY_PATH {{ cmake_library_path }})
{% endif %}
{% if cmake_include_path %}
list(PREPEND CMAKE_INCLUDE_PATH {{ cmake_include_path }})
{% endif %}
""")
host_req = self._conanfile.dependencies.host
build_req = self._conanfile.dependencies.direct_build
test_req = self._conanfile.dependencies.test

all_reqs = list(host_req.items()) + list(test_req.items()) + list(build_req.items())
# gen_folder = self._conanfile.generators_folder.replace("\\", "/")
# if not, test_cmake_add_subdirectory test fails
# content.append('set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)')
pkg_paths = {}
for req, dep in list(host_req.items()) + list(build_req.items()) + list(test_req.items()):
for req, dep in all_reqs:
cmake_find_mode = self._cmakedeps.get_property("cmake_find_mode", dep)
cmake_find_mode = cmake_find_mode or FIND_MODE_CONFIG
cmake_find_mode = cmake_find_mode.lower()
Expand All @@ -214,26 +239,17 @@ def generate(self):
# content.append(f'set({pkg_name}_ROOT "{gen_folder}")')
pkg_paths[pkg_name] = "${CMAKE_CURRENT_LIST_DIR}"

# CMAKE_PROGRAM_PATH
cmake_program_path = {}
for req, dep in list(host_req.items()) + list(test_req.items()) + list(build_req.items()):
if not req.direct:
continue
cppinfo = dep.cpp_info.aggregated_components()
if not cppinfo.bindirs:
continue
previous = cmake_program_path.get(req.ref.name)
if previous:
self._conanfile.output.info(f"There is already a '{req.ref}' package "
f"contributing to CMAKE_PROGRAM_PATH. The one with "
f"build={req.build} test={req.test} will be used")

cmake_program_path[req.ref.name] = cppinfo.bindirs
cmake_program_path = [d for dirs in cmake_program_path.values() for d in dirs]
# CMAKE_PROGRAM_PATH | CMAKE_LIBRARY_PATH | CMAKE_INCLUDE_PATH
cmake_program_path = self._get_cmake_paths([(req, dep) for req, dep in all_reqs if req.direct], "bindirs")
cmake_library_path = self._get_cmake_paths(host_req.items(), "libdirs")
cmake_include_path = self._get_cmake_paths(host_req.items(), "includedirs")
franramirez688 marked this conversation as resolved.
Show resolved Hide resolved

context = {"host_runtime_dirs": self._get_host_runtime_dirs(),
"pkg_paths": pkg_paths,
"cmake_program_path": _join_paths(self._conanfile, cmake_program_path)}
"cmake_program_path": _join_paths(self._conanfile, cmake_program_path),
"cmake_library_path": _join_paths(self._conanfile, cmake_library_path),
"cmake_include_path": _join_paths(self._conanfile, cmake_include_path),
}
content = Template(template, trim_blocks=True, lstrip_blocks=True).render(context)
save(self._conanfile, self._conan_cmakedeps_paths, content)

Expand Down
92 changes: 92 additions & 0 deletions test/integration/toolchains/cmake/cmakedeps2/test_cmakedeps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import re
import textwrap

from conan.test.utils.tools import TestClient

new_value = "will_break_next"


def test_cmakedeps_direct_deps_paths():
c = TestClient()
conanfile = textwrap.dedent("""
import os
from conan.tools.files import copy
from conan import ConanFile
class TestConan(ConanFile):
name = "lib"
version = "1.0"
def package_info(self):
self.cpp_info.includedirs = ["myincludes"]
self.cpp_info.libdirs = ["mylib"]
""")
c.save({"conanfile.py": conanfile})
c.run("create .")
conanfile = textwrap.dedent(f"""
from conan import ConanFile
from conan.tools.cmake import CMake
class PkgConan(ConanFile):
requires = "lib/1.0"
settings = "os", "arch", "compiler", "build_type"
generators = "CMakeDeps"
def build(self):
cmake = CMake(self)
cmake.configure()
""")
c.save({"conanfile.py": conanfile}, clean_first=True)
c.run(f"install . -c tools.cmake.cmakedeps:new={new_value}")
cmake_paths = c.load("conan_cmakedeps_paths.cmake")
assert re.search(r"list\(PREPEND CMAKE_PROGRAM_PATH \".*/bin\"", cmake_paths) # default
assert re.search(r"list\(PREPEND CMAKE_LIBRARY_PATH \".*/mylib\"", cmake_paths)
assert re.search(r"list\(PREPEND CMAKE_INCLUDE_PATH \".*/myincludes\"", cmake_paths)


def test_cmakedeps_transitive_paths():
c = TestClient()
conanfile = textwrap.dedent("""
import os
from conan.tools.files import copy
from conan import ConanFile
class TestConan(ConanFile):
name = "liba"
version = "1.0"
def package_info(self):
self.cpp_info.includedirs = ["includea"]
self.cpp_info.libdirs = ["liba"]
self.cpp_info.bindirs = ["bina"]
""")
c.save({"conanfile.py": conanfile})
c.run("create .")
conanfile = textwrap.dedent("""
import os
from conan.tools.files import copy
from conan import ConanFile
class TestConan(ConanFile):
name = "libb"
version = "1.0"
requires = "liba/1.0"
def package_info(self):
self.cpp_info.includedirs = ["includeb"]
self.cpp_info.libdirs = ["libb"]
self.cpp_info.bindirs = ["binb"]
""")
c.save({"conanfile.py": conanfile})
c.run("create .")
conanfile = textwrap.dedent(f"""
from conan import ConanFile
from conan.tools.cmake import CMake
class PkgConan(ConanFile):
requires = "libb/1.0"
settings = "os", "arch", "compiler", "build_type"
generators = "CMakeDeps"
def build(self):
cmake = CMake(self)
cmake.configure()
""")
c.save({"conanfile.py": conanfile}, clean_first=True)
c.run(f"install . -c tools.cmake.cmakedeps:new={new_value}")
cmake_paths = c.load("conan_cmakedeps_paths.cmake")
cmake_paths.replace("\\", "/")
assert re.search(r"list\(PREPEND CMAKE_PROGRAM_PATH \".*/libb.*/p/binb\"\)", cmake_paths)
assert not re.search(r"list\(PREPEND CMAKE_PROGRAM_PATH /bina\"", cmake_paths)
assert re.search(r"list\(PREPEND CMAKE_LIBRARY_PATH \".*/libb.*/p/libb\" \".*/liba.*/p/liba\"\)", cmake_paths)
assert re.search(r"list\(PREPEND CMAKE_INCLUDE_PATH \".*/libb.*/p/includeb\" \".*/liba.*/p/includea\"\)", cmake_paths)
Loading