Repository: conan-io/conan

Issue:
CMake build wrapper should set PKG_CONFIG_PATH

- [x] I've read the [CONTRIBUTING guide](https://raw.githubusercontent.com/conan-io/conan/develop/.github/CONTRIBUTING.md).
- [x] I've specified the Conan version, operating system version and any tool that can be relevant.
- [x] I've explained the steps to reproduce the error or the motivation/use case of the question/suggestion.

conan version 1.0.4 or master

A lot of cmake scripts use both `find_package` (`FindFoo.cmake`-based) and `pkg_check_modules` (`pkg-config`-based). CMake build wrapper should automatically provide `PKG_CONFIG_PATH` env var set to build directory or to recipe-provided paths. Exact same behavior is seen in `AutoToolsBuildEnviroment` or `Meson`. `CMake` should not be an exception.

Discussion:

Patch:
diff --git a/conans/client/build/autotools_environment.py b/conans/client/build/autotools_environment.py
index 924161e9c..9bf4bd3e8 100644
--- a/conans/client/build/autotools_environment.py
+++ b/conans/client/build/autotools_environment.py
@@ -14,6 +14,7 @@ from conans.client.tools.win import unix_path
 from conans.tools import (environment_append, args_to_string, cpu_count, cross_building,
                           detected_architecture, get_gnu_triplet)
 from conans.errors import ConanException
+from conans.util.files import get_abs_path
 
 
 class AutoToolsBuildEnvironment(object):
@@ -131,7 +132,9 @@ class AutoToolsBuildEnvironment(object):
                 triplet_args.append("--target=%s" % (target or self.target))
 
         if pkg_config_paths:
-            pkg_env = {"PKG_CONFIG_PATH": os.pathsep.join(pkg_config_paths)}
+            pkg_env = {"PKG_CONFIG_PATH":
+                       os.pathsep.join(get_abs_path(f, self._conanfile.install_folder)
+                                       for f in pkg_config_paths)}
         else:
             # If we are using pkg_config generator automate the pcs location, otherwise it could
             # read wrong files
diff --git a/conans/client/build/cmake.py b/conans/client/build/cmake.py
index 9964d0836..b5f8cb843 100644
--- a/conans/client/build/cmake.py
+++ b/conans/client/build/cmake.py
@@ -12,7 +12,7 @@ from conans.errors import ConanException
 from conans.model.conan_file import ConanFile
 from conans.model.version import Version
 from conans.util.env_reader import get_env
-from conans.util.files import mkdir
+from conans.util.files import mkdir, get_abs_path
 from conans.tools import cpu_count, args_to_string
 from conans import tools
 from conans.util.log import logger
@@ -28,7 +28,8 @@ def _get_env_cmake_system_name():
 class CMake(object):
 
     def __init__(self, conanfile, generator=None, cmake_system_name=True,
-                 parallel=True, build_type=None, toolset=None, make_program=None, set_cmake_flags=False):
+                 parallel=True, build_type=None, toolset=None, make_program=None,
+                 set_cmake_flags=False):
         """
         :param settings_or_conanfile: Conanfile instance (or settings for retro compatibility)
         :param generator: Generator name to use or none to autodetect
@@ -370,7 +371,8 @@ class CMake(object):
             self._conanfile.run(command)
 
     def configure(self, args=None, defs=None, source_dir=None, build_dir=None,
-                  source_folder=None, build_folder=None, cache_build_folder=None):
+                  source_folder=None, build_folder=None, cache_build_folder=None,
+                  pkg_config_paths=None):
 
         # TODO: Deprecate source_dir and build_dir in favor of xxx_folder
         if not self._conanfile.should_configure:
@@ -387,12 +389,26 @@ class CMake(object):
             defs_to_string(defs),
             args_to_string([source_dir])
         ])
-        command = "cd %s && cmake %s" % (args_to_string([self.build_dir]), arg_list)
-        if platform.system() == "Windows" and self.generator == "MinGW Makefiles":
-            with tools.remove_from_path("sh"):
-                self._run(command)
+
+
+        if pkg_config_paths:
+            pkg_env = {"PKG_CONFIG_PATH":
+                       os.pathsep.join(get_abs_path(f, self._conanfile.install_folder)
+                                       for f in pkg_config_paths)}
         else:
-            self._run(command)
+            # If we are using pkg_config generator automate the pcs location, otherwise it could
+            # read wrong files
+            set_env = "pkg_config" in self._conanfile.generators \
+                      and "PKG_CONFIG_PATH" not in os.environ
+            pkg_env = {"PKG_CONFIG_PATH": self._conanfile.install_folder} if set_env else {}
+
+        with tools.environment_append(pkg_env):
+            command = "cd %s && cmake %s" % (args_to_string([self.build_dir]), arg_list)
+            if platform.system() == "Windows" and self.generator == "MinGW Makefiles":
+                with tools.remove_from_path("sh"):
+                    self._conanfile.run(command)
+            else:
+                self._conanfile.run(command)
 
     def build(self, args=None, build_dir=None, target=None):
         if not self._conanfile.should_build:
diff --git a/conans/client/build/meson.py b/conans/client/build/meson.py
index 1545a59d7..b8a7ff4b3 100644
--- a/conans/client/build/meson.py
+++ b/conans/client/build/meson.py
@@ -4,7 +4,7 @@ from conans import tools
 from conans.client import join_arguments, defs_to_string
 from conans.errors import ConanException
 from conans.tools import args_to_string
-from conans.util.files import mkdir
+from conans.util.files import mkdir, get_abs_path
 
 
 class Meson(object):
@@ -53,14 +53,6 @@ class Meson(object):
     def build_folder(self, value):
         self.build_dir = value
 
-    @staticmethod
-    def _get_dir(folder, origin):
-        if folder:
-            if os.path.isabs(folder):
-                return folder
-            return os.path.join(origin, folder)
-        return origin
-
     def _get_dirs(self, source_folder, build_folder, source_dir, build_dir, cache_build_folder):
         if (source_folder or build_folder) and (source_dir or build_dir):
             raise ConanException("Use 'build_folder'/'source_folder'")
@@ -69,11 +61,11 @@ class Meson(object):
             build_ret = build_dir or self.build_dir or self._conanfile.build_folder
             source_ret = source_dir or self._conanfile.source_folder
         else:
-            build_ret = self._get_dir(build_folder, self._conanfile.build_folder)
-            source_ret = self._get_dir(source_folder, self._conanfile.source_folder)
+            build_ret = get_abs_path(build_folder, self._conanfile.build_folder)
+            source_ret = get_abs_path(source_folder, self._conanfile.source_folder)
 
         if self._conanfile.in_local_cache and cache_build_folder:
-            build_ret = self._get_dir(cache_build_folder, self._conanfile.build_folder)
+            build_ret = get_abs_path(cache_build_folder, self._conanfile.build_folder)
 
         return source_ret, build_ret
 
@@ -90,7 +82,7 @@ class Meson(object):
                                                     cache_build_folder)
 
         if pkg_config_paths:
-            pc_paths = os.pathsep.join(self._get_dir(f, self._conanfile.install_folder)
+            pc_paths = os.pathsep.join(get_abs_path(f, self._conanfile.install_folder)
                                        for f in pkg_config_paths)
         else:
             pc_paths = self._conanfile.install_folder
diff --git a/conans/util/files.py b/conans/util/files.py
index d8492cd72..8c6a859a1 100644
--- a/conans/util/files.py
+++ b/conans/util/files.py
@@ -181,6 +181,14 @@ def relative_dirs(path):
     return ret
 
 
+def get_abs_path(folder, origin):
+    if folder:
+        if os.path.isabs(folder):
+            return folder
+        return os.path.join(origin, folder)
+    return origin
+
+
 def _change_permissions(func, path, exc_info):
     if not os.access(path, os.W_OK):
         os.chmod(path, stat.S_IWUSR)


Test patch:
diff --git a/conans/test/build_helpers/autotools_configure_test.py b/conans/test/build_helpers/autotools_configure_test.py
index fed69a0a3..9f0fcd983 100644
--- a/conans/test/build_helpers/autotools_configure_test.py
+++ b/conans/test/build_helpers/autotools_configure_test.py
@@ -1,16 +1,18 @@
+import os
 import platform
 import unittest
+from collections import namedtuple
 
-from conans.client.build.autotools_environment import AutoToolsBuildEnvironment
 from conans import tools
+from conans.client.build.autotools_environment import AutoToolsBuildEnvironment
 from conans.client.tools.oss import cpu_count
+from conans.model.ref import ConanFileReference
+from conans.model.settings import Settings
 from conans.paths import CONANFILE
-from conans.test.utils.conanfile import MockConanfile, MockSettings, MockOptions
+from conans.test.build_helpers.cmake_test import ConanFileMock
 from conans.test.util.tools_test import RunnerMock
+from conans.test.utils.conanfile import MockConanfile, MockSettings, MockOptions
 from conans.test.utils.tools import TestClient
-from conans.test.build_helpers.cmake_test import ConanFileMock
-from conans.model.settings import Settings
-from collections import namedtuple
 
 
 class AutoToolsConfigureTest(unittest.TestCase):
@@ -416,9 +418,12 @@ class HelloConan(ConanFile):
         self.assertIn("PKG_CONFIG_PATH=%s" % client.client_cache.conan_folder, client.out)
 
         client.save({CONANFILE: conanfile % ("'pkg_config'",
-                                             "pkg_config_paths=['/tmp/hello', '/tmp/foo']")})
+                                             "pkg_config_paths=['/tmp/hello', 'foo']")})
         client.run("create . conan/testing")
-        self.assertIn("PKG_CONFIG_PATH=/tmp/hello:/tmp/foo", client.out)
+        ref = ConanFileReference.loads("Hello/1.2.1@conan/testing")
+        builds_folder = client.client_cache.builds(ref)
+        bf = os.path.join(builds_folder, os.listdir(builds_folder)[0])
+        self.assertIn("PKG_CONFIG_PATH=/tmp/hello:%s/foo" % bf, client.out)
 
     def cross_build_command_test(self):
         runner = RunnerMock()
diff --git a/conans/test/build_helpers/cmake_test.py b/conans/test/build_helpers/cmake_test.py
index 812c53444..09aab1631 100644
--- a/conans/test/build_helpers/cmake_test.py
+++ b/conans/test/build_helpers/cmake_test.py
@@ -688,6 +688,38 @@ build_type: [ Release]
         cmake.configure()
         self.assertNotIn(self.tempdir, conanfile.path)
 
+    def test_pkg_config_path(self):
+        conanfile = ConanFileMock()
+        conanfile.generators = ["pkg_config"]
+        conanfile.install_folder = "/my_install/folder/"
+        settings = Settings.loads(default_settings_yml)
+        settings.os = "Windows"
+        settings.compiler = "Visual Studio"
+        settings.compiler.version = "12"
+        settings.arch = "x86"
+        conanfile.settings = settings
+        cmake = CMake(conanfile)
+        cmake.configure()
+        self.assertEquals(conanfile.captured_env["PKG_CONFIG_PATH"], "/my_install/folder/")
+
+        conanfile.generators = []
+        cmake = CMake(conanfile)
+        cmake.configure()
+        self.assertNotIn("PKG_CONFIG_PATH", conanfile.captured_env)
+
+        cmake = CMake(conanfile)
+        cmake.configure(pkg_config_paths=["reldir1", "/abspath2/to/other"])
+        self.assertEquals(conanfile.captured_env["PKG_CONFIG_PATH"],
+                          os.path.pathsep.join(["/my_install/folder/reldir1",
+                                                "/abspath2/to/other"]))
+
+        # If there is already a PKG_CONFIG_PATH do not set it
+        conanfile.generators = ["pkg_config"]
+        cmake = CMake(conanfile)
+        with tools.environment_append({"PKG_CONFIG_PATH": "do_not_mess_with_this"}):
+            cmake.configure()
+            self.assertEquals(conanfile.captured_env["PKG_CONFIG_PATH"], "do_not_mess_with_this")
+
     def test_shared(self):
         settings = Settings.loads(default_settings_yml)
         settings.os = "Windows"
@@ -843,7 +875,10 @@ class ConanFileMock(ConanFile):
         self.should_configure = True
         self.should_build = True
         self.should_install = True
+        self.generators = []
+        self.captured_env = {}
 
     def run(self, command):
         self.command = command
         self.path = os.environ["PATH"]
+        self.captured_env = {key: value for key, value in os.environ.items()}