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

[CMakeToolchain][env] Added PKG_CONFIG_PATH env variable and PKG_CONFIG_EXECUTABLE one #12513

Merged
merged 11 commits into from
Nov 16, 2022
31 changes: 30 additions & 1 deletion conan/tools/cmake/toolchain/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from conan.tools.cmake.toolchain import CONAN_TOOLCHAIN_FILENAME
from conan.tools.intel import IntelCC
from conan.tools.microsoft.visual import is_msvc, msvc_version_to_toolset_version
from conans.client.subsystems import deduce_subsystem, WINDOWS
from conans.errors import ConanException
from conans.util.files import load

Expand Down Expand Up @@ -153,7 +154,7 @@ class FPicBlock(Block):
template = textwrap.dedent("""
{% if fpic %}
message(STATUS "Conan toolchain: Setting CMAKE_POSITION_INDEPENDENT_CODE={{ fpic }} (options.fPIC)")
set(CMAKE_POSITION_INDEPENDENT_CODE {{ fpic }} CACHE BOOL "Position independent code")
set(CMAKE_POSITION_INDEPENDENT_CODE {{ fpic }} CACHE BOOL "Position independent code")
{% endif %}
""")

Expand Down Expand Up @@ -559,6 +560,34 @@ def context(self):
}


class PkgConfigBlock(Block):
template = textwrap.dedent("""
{% if pkg_config %}
set(PKG_CONFIG_EXECUTABLE {{ pkg_config }} CACHE FILEPATH "pkg-config executable")
{% endif %}
{% if pkg_config_path %}
if (DEFINED ENV{PKG_CONFIG_PATH})
set(ENV{PKG_CONFIG_PATH} "{{ pkg_config_path }}$ENV{PKG_CONFIG_PATH}")
else()
set(ENV{PKG_CONFIG_PATH} "{{ pkg_config_path }}")
endif()
{% endif %}
""")

def context(self):
pkg_config = self._conanfile.conf.get("tools.gnu:pkg_config", check_type=str)
if pkg_config:
pkg_config = pkg_config.replace("\\", "/")
pkg_config_path = self._conanfile.generators_folder
if pkg_config_path:
# hardcoding scope as "build"
subsystem = deduce_subsystem(self._conanfile, "build")
pathsep = ":" if subsystem != WINDOWS else ";"
pkg_config_path = pkg_config_path.replace("\\", "/") + pathsep
Copy link
Contributor

Choose a reason for hiding this comment

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

I like this! :D Matches the documentation - and still allows (a few lines above in the template block) to fall back on anything the user may have previously specified

PKG_CONFIG_PATH

A colon-separated (on Windows, semicolon-separated) list of directories to search for .pc files. The default directory will always be searched after searching the path; the default is libdir/pkgconfig:datadir/pkgconfig where libdir is the libdir where pkg-config and datadir is the datadir where pkg-config was installed.

return {"pkg_config": pkg_config,
"pkg_config_path": pkg_config_path}


class UserToolchain(Block):
template = textwrap.dedent("""
{% for user_toolchain in paths %}
Expand Down
5 changes: 3 additions & 2 deletions conan/tools/cmake/toolchain/toolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
from conan.tools.cmake.toolchain import CONAN_TOOLCHAIN_FILENAME
from conan.tools.cmake.toolchain.blocks import ToolchainBlocks, UserToolchain, GenericSystemBlock, \
AndroidSystemBlock, AppleSystemBlock, FPicBlock, ArchitectureBlock, GLibCXXBlock, VSRuntimeBlock, \
CppStdBlock, ParallelBlock, CMakeFlagsInitBlock, TryCompileBlock, FindFiles, SkipRPath, \
SharedLibBock, OutputDirsBlock, ExtraFlagsBlock
CppStdBlock, ParallelBlock, CMakeFlagsInitBlock, TryCompileBlock, FindFiles, PkgConfigBlock, \
SkipRPath, SharedLibBock, OutputDirsBlock, ExtraFlagsBlock
from conan.tools.intel import IntelCC
from conan.tools.microsoft import VCVars
from conan.tools.microsoft.visual import vs_ide_version
Expand Down Expand Up @@ -139,6 +139,7 @@ def __init__(self, conanfile, generator=None):
("cmake_flags_init", CMakeFlagsInitBlock),
("try_compile", TryCompileBlock),
("find_paths", FindFiles),
("pkg_config", PkgConfigBlock),
("rpath", SkipRPath),
("shared", SharedLibBock),
("output_dirs", OutputDirsBlock)])
Expand Down
66 changes: 61 additions & 5 deletions conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -1099,7 +1099,7 @@ def test_cmake_toolchain_vars_when_option_declared():

t.run("new mylib/1.0 --template cmake_lib")
t.save({"CMakeLists.txt": cmakelists})

# The generated toolchain should set `BUILD_SHARED_LIBS` to `OFF`,
# and `CMAKE_POSITION_INDEPENDENT_CODE` to `ON` and the calls to
# `option()` in the CMakeLists.txt should respect the existing values.
Expand All @@ -1112,7 +1112,7 @@ def test_cmake_toolchain_vars_when_option_declared():
assert "mylib target type: STATIC_LIBRARY" in t.out
assert f"mylib position independent code: {fpic_cmake_value}" in t.out

# When building manually, ensure the value passed by the toolchain overrides the ones in
# When building manually, ensure the value passed by the toolchain overrides the ones in
# the CMakeLists
fpic_option = "-o mylib:fPIC=False" if platform.system() != "Windows" else ""
t.run(f"install . -o mylib:shared=False {fpic_option}")
Expand Down Expand Up @@ -1198,10 +1198,10 @@ class PkgConan(ConanFile):

def layout(self):
cmake_layout(self)

def requirements(self):
self.requires("foobar/1.0")

def build_requirements(self):
self.tool_requires("foobar/1.0")
""")
Expand All @@ -1227,7 +1227,63 @@ def build_requirements(self):

with client.chdir("build"):
client.run_command("cmake .. -DCMAKE_TOOLCHAIN_FILE=generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release")
# Verify binary executable is found from build context package,
# Verify binary executable is found from build context package,
# and library comes from host context package
assert f"package/{build_context_package_id}/bin/foobin" in client.out
assert f"package/{host_context_package_id}/include" in client.out


@pytest.mark.tool_pkg_config
def test_cmaketoolchain_and_pkg_config_path():
"""
Lightweight test which is loading a dependency as a *.pc file through
Copy link
Member

Choose a reason for hiding this comment

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

Nice job with this test!

CMake thanks to pkg_check_modules macro.
It's working because PKG_CONFIG_PATH env variable is being loaded automatically
by the CMakeToolchain generator.
"""
client = TestClient()
dep = textwrap.dedent("""
from conan import ConanFile
class DepConan(ConanFile):
name = "dep"
version = "1.0"
def package_info(self):
self.cpp_info.libs = ["dep"]
""")
pkg = textwrap.dedent("""
from conan import ConanFile
from conan.tools.cmake import CMake, cmake_layout
class HelloConan(ConanFile):
name = "pkg"
version = "1.0"
settings = "os", "compiler", "build_type", "arch"
generators = "PkgConfigDeps", "CMakeToolchain"
exports_sources = "CMakeLists.txt"

def requirements(self):
self.requires("dep/1.0")

def layout(self):
cmake_layout(self)

def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
""")
cmakelists = textwrap.dedent("""
cmake_minimum_required(VERSION 3.15)
project(pkg CXX)

find_package(PkgConfig REQUIRED)
# We should have PKG_CONFIG_PATH created in the current environment
pkg_check_modules(DEP REQUIRED IMPORTED_TARGET dep)
""")
client.save({
"dep/conanfile.py": dep,
"pkg/conanfile.py": pkg,
"pkg/CMakeLists.txt": cmakelists
})
client.run("create dep/conanfile.py")
client.run("create pkg/conanfile.py")
assert "Found dep, version 1.0" in client.out
franramirez688 marked this conversation as resolved.
Show resolved Hide resolved
26 changes: 26 additions & 0 deletions conans/test/integration/toolchains/cmake/test_cmaketoolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -758,3 +758,29 @@ def layout(self):
presets = json.loads(client.load("build/14/generators/CMakePresets.json"))
assert "architecture" in presets["configurePresets"][0]
assert "toolset" not in presets["configurePresets"][0]


def test_pkg_config_block():
os_ = platform.system()
os_ = "Macos" if os_ == "Darwin" else os_
profile = textwrap.dedent("""
[settings]
os=%s
arch=x86_64

[conf]
tools.gnu:pkg_config=/usr/local/bin/pkg-config
""" % os_)

client = TestClient(path_with_spaces=False)
conanfile = GenConanfile().with_settings("os", "arch")\
.with_generator("CMakeToolchain")
client.save({"conanfile.py": conanfile,
"profile": profile})
client.run("install . -pr:b profile -pr:h profile")
toolchain = client.load("conan_toolchain.cmake")
assert 'set(PKG_CONFIG_EXECUTABLE /usr/local/bin/pkg-config CACHE FILEPATH ' in toolchain
pathsep = ":" if os_ != "Windows" else ";"
pkg_config_path_set = 'set(ENV{PKG_CONFIG_PATH} "%s$ENV{PKG_CONFIG_PATH}")' % \
(client.current_folder.replace("\\", "/") + pathsep)
assert pkg_config_path_set in toolchain