1import os 2 3from conans.client.build.compiler_flags import rpath_flags, format_frameworks, format_framework_paths 4from conans.client.tools.oss import get_build_os_arch 5from conans.model import Generator 6from conans.model.conan_generator import GeneratorComponentsMixin 7 8""" 9PC FILE EXAMPLE: 10 11prefix=/usr 12exec_prefix=${prefix} 13libdir=${exec_prefix}/lib 14includedir=${prefix}/include 15 16Name: my-project 17Description: Some brief but informative description 18Version: 1.2.3 19Libs: -L${libdir} -lmy-project-1 -linkerflag -Wl,-rpath=${libdir} 20Cflags: -I${includedir}/my-project-1 21Requires: glib-2.0 >= 2.40 gio-2.0 >= 2.42 nice >= 0.1.6 22Requires.private: gthread-2.0 >= 2.40 23""" 24 25 26class PkgConfigGenerator(GeneratorComponentsMixin, Generator): 27 name = "pkg_config" 28 29 @property 30 def filename(self): 31 return None 32 33 @property 34 def compiler(self): 35 return self.conanfile.settings.get_safe("compiler") 36 37 def _get_components(self, pkg_name, cpp_info): 38 generator_components = super(PkgConfigGenerator, self)._get_components(pkg_name, cpp_info) 39 ret = [] 40 for comp_genname, comp, comp_requires_gennames in generator_components: 41 ret.append((comp_genname, comp, [it[1] for it in comp_requires_gennames])) 42 return ret 43 44 @property 45 def content(self): 46 ret = {} 47 for depname, cpp_info in self.deps_build_info.dependencies: 48 pkg_genname = cpp_info.get_name(PkgConfigGenerator.name) 49 self._validate_components(cpp_info) 50 if not cpp_info.components: 51 public_deps = self.get_public_deps(cpp_info) 52 deps_names = [self._get_require_name(*it)[1] for it in public_deps] 53 ret["%s.pc" % pkg_genname] = self._pc_file_content(pkg_genname, cpp_info, deps_names) 54 else: 55 components = self._get_components(depname, cpp_info) 56 for comp_genname, comp, comp_requires_gennames in components: 57 ret["%s.pc" % comp_genname] = self._pc_file_content( 58 "%s-%s" % (pkg_genname, comp_genname), 59 comp, 60 comp_requires_gennames) 61 comp_gennames = [comp_genname for comp_genname, _, _ in components] 62 if pkg_genname not in comp_gennames: 63 ret["%s.pc" % pkg_genname] = self.global_pc_file_contents(pkg_genname, cpp_info, 64 comp_gennames) 65 return ret 66 67 def _pc_file_content(self, name, cpp_info, requires_gennames): 68 prefix_path = cpp_info.rootpath.replace("\\", "/") 69 lines = ['prefix=%s' % prefix_path] 70 71 libdir_vars = [] 72 dir_lines, varnames = self._generate_dir_lines(prefix_path, "libdir", cpp_info.lib_paths) 73 if dir_lines: 74 libdir_vars = varnames 75 lines.extend(dir_lines) 76 77 includedir_vars = [] 78 dir_lines, varnames = self._generate_dir_lines(prefix_path, "includedir", 79 cpp_info.include_paths) 80 if dir_lines: 81 includedir_vars = varnames 82 lines.extend(dir_lines) 83 84 pkg_config_custom_content = cpp_info.get_property("pkg_config_custom_content") 85 if pkg_config_custom_content: 86 lines.append(pkg_config_custom_content) 87 88 lines.append("") 89 lines.append("Name: %s" % name) 90 description = cpp_info.description or "Conan package: %s" % name 91 lines.append("Description: %s" % description) 92 lines.append("Version: %s" % cpp_info.version) 93 libdirs_flags = ['-L"${%s}"' % name for name in libdir_vars] 94 libnames_flags = ["-l%s " % name for name in (cpp_info.libs + cpp_info.system_libs)] 95 shared_flags = cpp_info.sharedlinkflags + cpp_info.exelinkflags 96 97 os_build, _ = get_build_os_arch(self.conanfile) 98 if not hasattr(self.conanfile, 'settings_build'): 99 os_build = os_build or self.conanfile.settings.get_safe("os") 100 101 rpaths = rpath_flags(self.conanfile.settings, os_build, 102 ["${%s}" % libdir for libdir in libdir_vars]) 103 frameworks = format_frameworks(cpp_info.frameworks, self.conanfile.settings) 104 framework_paths = format_framework_paths(cpp_info.framework_paths, self.conanfile.settings) 105 106 lines.append("Libs: %s" % _concat_if_not_empty([libdirs_flags, 107 libnames_flags, 108 shared_flags, 109 rpaths, 110 frameworks, 111 framework_paths])) 112 include_dirs_flags = ['-I"${%s}"' % name for name in includedir_vars] 113 114 lines.append("Cflags: %s" % _concat_if_not_empty( 115 [include_dirs_flags, 116 cpp_info.cxxflags, 117 cpp_info.cflags, 118 ["-D%s" % d for d in cpp_info.defines]])) 119 120 if requires_gennames: 121 public_deps = " ".join(requires_gennames) 122 lines.append("Requires: %s" % public_deps) 123 return "\n".join(lines) + "\n" 124 125 @staticmethod 126 def global_pc_file_contents(name, cpp_info, comp_gennames): 127 lines = ["Name: %s" % name] 128 description = cpp_info.description or "Conan package: %s" % name 129 lines.append("Description: %s" % description) 130 lines.append("Version: %s" % cpp_info.version) 131 132 if comp_gennames: 133 public_deps = " ".join(comp_gennames) 134 lines.append("Requires: %s" % public_deps) 135 return "\n".join(lines) + "\n" 136 137 @staticmethod 138 def _generate_dir_lines(prefix_path, varname, dirs): 139 lines = [] 140 varnames = [] 141 for i, directory in enumerate(dirs): 142 directory = os.path.normpath(directory).replace("\\", "/") 143 name = varname if i == 0 else "%s%d" % (varname, (i + 1)) 144 prefix = "" 145 if not os.path.isabs(directory): 146 prefix = "${prefix}/" 147 elif directory.startswith(prefix_path): 148 prefix = "${prefix}/" 149 directory = os.path.relpath(directory, prefix_path).replace("\\", "/") 150 lines.append("%s=%s%s" % (name, prefix, directory)) 151 varnames.append(name) 152 return lines, varnames 153 154 155def _concat_if_not_empty(groups): 156 return " ".join([param for group in groups for param in group if param and param.strip()]) 157