forked from conan-io/conan
/
pkgconfigdeps.py
196 lines (164 loc) · 8.35 KB
/
pkgconfigdeps.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
"""
PkgConfigDeps Conan generator
- PC FILE EXAMPLE:
prefix=/usr
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: my-project
Description: Some brief but informative description
Version: 1.2.3
Libs: -L${libdir} -lmy-project-1 -linkerflag -Wl,-rpath=${libdir}
Cflags: -I${includedir}/my-project-1
Requires: glib-2.0 >= 2.40 gio-2.0 >= 2.42 nice >= 0.1.6
Requires.private: gthread-2.0 >= 2.40
"""
import os
from conan.tools.gnu.gnudeps_flags import GnuDepsFlags
from conans.errors import ConanException
from conans.util.files import save
def _concat_if_not_empty(groups):
return " ".join([param for group in groups for param in group if param and param.strip()])
def get_target_namespace(req):
ret = req.new_cpp_info.get_property("pkg_config_name", "PkgConfigDeps")
return ret or req.ref.name
def get_component_alias(req, comp_name):
if comp_name not in req.new_cpp_info.components:
# foo::foo might be referencing the root cppinfo
if req.ref.name == comp_name:
return get_target_namespace(req)
raise ConanException("Component '{name}::{cname}' not found in '{name}' "
"package requirement".format(name=req.ref.name, cname=comp_name))
ret = req.new_cpp_info.components[comp_name].get_property("pkg_config_name", "PkgConfigDeps")
return ret or comp_name
class PkgConfigDeps(object):
def __init__(self, conanfile):
self._conanfile = conanfile
def _get_require_comp_name(self, dep, req):
pkg_name = dep.ref.name
pkg, comp_name = req.split("::") if "::" in req else (pkg_name, req)
req = dep.dependencies.direct_host[pkg]
cmp_name = get_component_alias(req, comp_name)
return cmp_name
def _get_components(self, dep):
ret = []
for comp_name, comp in dep.new_cpp_info.get_sorted_components().items():
comp_genname = get_component_alias(dep, comp_name)
comp_requires_gennames = []
for require in comp.requires:
comp_requires_gennames.append(self._get_require_comp_name(dep, require))
ret.append((comp_genname, comp, comp_requires_gennames))
return ret
def _get_public_require_deps(self, dep):
public_comp_deps = []
for require in dep.new_cpp_info.requires:
if "::" in require: # Points to a component of a different package
pkg, cmp_name = require.split("::")
req = dep.dependencies.direct_host[pkg]
public_comp_deps.append(
(get_target_namespace(req), get_component_alias(req, cmp_name)))
else: # Points to a component of same package
public_comp_deps.append((get_target_namespace(dep),
get_component_alias(dep, require)))
return public_comp_deps
@property
def content(self):
ret = {}
host_req = self._conanfile.dependencies.host
for require, dep in host_req.items():
pkg_genname = get_target_namespace(dep)
if dep.new_cpp_info.has_components:
components = self._get_components(dep)
for comp_genname, comp_cpp_info, comp_requires_gennames in components:
pkg_comp_genname = "%s-%s" % (pkg_genname, comp_genname)
ret["%s.pc" % pkg_comp_genname] = self._pc_file_content(
pkg_comp_genname, comp_cpp_info,
comp_requires_gennames, dep.package_folder,
dep.ref.version)
comp_gennames = [comp_genname for comp_genname, _, _ in components]
if pkg_genname not in comp_gennames:
ret["%s.pc" % pkg_genname] = self._global_pc_file_contents(pkg_genname,
dep,
comp_gennames)
else:
require_public_deps = [_d for _, _d in
self._get_public_require_deps(dep)]
ret["%s.pc" % pkg_genname] = self._pc_file_content(pkg_genname, dep.new_cpp_info,
require_public_deps,
dep.package_folder,
dep.ref.version)
return ret
def _pc_file_content(self, name, cpp_info, requires_gennames, package_folder, version):
prefix_path = package_folder.replace("\\", "/")
lines = ['prefix=%s' % prefix_path]
gnudeps_flags = GnuDepsFlags(self._conanfile, cpp_info)
libdir_vars = []
dir_lines, varnames = self._generate_dir_lines(prefix_path, "libdir", cpp_info.libdirs)
if dir_lines:
libdir_vars = varnames
lines.extend(dir_lines)
includedir_vars = []
dir_lines, varnames = self._generate_dir_lines(prefix_path, "includedir",
cpp_info.includedirs)
if dir_lines:
includedir_vars = varnames
lines.extend(dir_lines)
pkg_config_custom_content = cpp_info.get_property("pkg_config_custom_content",
"PkgConfigDeps")
if pkg_config_custom_content:
lines.append(pkg_config_custom_content)
lines.append("")
lines.append("Name: %s" % name)
description = self._conanfile.description or "Conan package: %s" % name
lines.append("Description: %s" % description)
lines.append("Version: %s" % version)
libdirs_flags = ['-L"${%s}"' % name for name in libdir_vars]
lib_paths = ["${%s}" % libdir for libdir in libdir_vars]
libnames_flags = ["-l%s " % name for name in (cpp_info.libs + cpp_info.system_libs)]
shared_flags = cpp_info.sharedlinkflags + cpp_info.exelinkflags
lines.append("Libs: %s" % _concat_if_not_empty([libdirs_flags,
libnames_flags,
shared_flags,
gnudeps_flags._rpath_flags(lib_paths),
gnudeps_flags.frameworks,
gnudeps_flags.framework_paths]))
include_dirs_flags = ['-I"${%s}"' % name for name in includedir_vars]
lines.append("Cflags: %s" % _concat_if_not_empty(
[include_dirs_flags,
cpp_info.cxxflags,
cpp_info.cflags,
["-D%s" % d for d in cpp_info.defines]]))
if requires_gennames:
public_deps = " ".join(requires_gennames)
lines.append("Requires: %s" % public_deps)
return "\n".join(lines) + "\n"
def _global_pc_file_contents(self, name, dep, comp_gennames):
lines = ["Name: %s" % name]
description = self._conanfile.description or "Conan package: %s" % name
lines.append("Description: %s" % description)
lines.append("Version: %s" % dep.ref.version)
if comp_gennames:
public_deps = " ".join(comp_gennames)
lines.append("Requires: %s" % public_deps)
return "\n".join(lines) + "\n"
@staticmethod
def _generate_dir_lines(prefix_path, varname, dirs):
lines = []
varnames = []
for i, directory in enumerate(dirs):
directory = os.path.normpath(directory).replace("\\", "/")
name = varname if i == 0 else "%s%d" % (varname, (i + 1))
prefix = ""
if not os.path.isabs(directory):
prefix = "${prefix}/"
elif directory.startswith(prefix_path):
prefix = "${prefix}/"
directory = os.path.relpath(directory, prefix_path).replace("\\", "/")
lines.append("%s=%s%s" % (name, prefix, directory))
varnames.append(name)
return lines, varnames
def generate(self):
# Current directory is the generators_folder
generator_files = self.content
for generator_file, content in generator_files.items():
save(generator_file, content)