pkgconfig.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. # Copyright 2015 The Meson development team
  2. # Licensed under the Apache License, Version 2.0 (the "License");
  3. # you may not use this file except in compliance with the License.
  4. # You may obtain a copy of the License at
  5. # http://www.apache.org/licenses/LICENSE-2.0
  6. # Unless required by applicable law or agreed to in writing, software
  7. # distributed under the License is distributed on an "AS IS" BASIS,
  8. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. # See the License for the specific language governing permissions and
  10. # limitations under the License.
  11. import os, types
  12. from pathlib import PurePath
  13. from .. import build
  14. from .. import dependencies
  15. from ..dependencies.misc import ThreadDependency
  16. from .. import mesonlib
  17. from .. import mlog
  18. from . import ModuleReturnValue
  19. from . import ExtensionModule
  20. from ..interpreterbase import permittedKwargs, FeatureNew, FeatureNewKwargs
  21. already_warned_objs = set()
  22. class DependenciesHelper:
  23. def __init__(self, state, name):
  24. self.state = state
  25. self.name = name
  26. self.pub_libs = []
  27. self.pub_reqs = []
  28. self.priv_libs = []
  29. self.priv_reqs = []
  30. self.cflags = []
  31. self.version_reqs = {}
  32. def add_pub_libs(self, libs):
  33. libs, reqs, cflags = self._process_libs(libs, True)
  34. self.pub_libs = libs + self.pub_libs # prepend to preserve dependencies
  35. self.pub_reqs += reqs
  36. self.cflags += cflags
  37. def add_priv_libs(self, libs):
  38. libs, reqs, _ = self._process_libs(libs, False)
  39. self.priv_libs = libs + self.priv_libs
  40. self.priv_reqs += reqs
  41. def add_pub_reqs(self, reqs):
  42. self.pub_reqs += self._process_reqs(reqs)
  43. def add_priv_reqs(self, reqs):
  44. self.priv_reqs += self._process_reqs(reqs)
  45. def _check_generated_pc_deprecation(self, obj):
  46. if not hasattr(obj, 'generated_pc_warn'):
  47. return
  48. name = obj.generated_pc_warn[0]
  49. if (name, obj.name) in already_warned_objs:
  50. return
  51. mlog.deprecation('Library', mlog.bold(obj.name), 'was passed to the '
  52. '"libraries" keyword argument of a previous call '
  53. 'to generate() method instead of first positional '
  54. 'argument.', 'Adding', mlog.bold(obj.generated_pc),
  55. 'to "Requires" field, but this is a deprecated '
  56. 'behaviour that will change in a future version '
  57. 'of Meson. Please report the issue if this '
  58. 'warning cannot be avoided in your case.',
  59. location=obj.generated_pc_warn[1])
  60. already_warned_objs.add((name, obj.name))
  61. def _process_reqs(self, reqs):
  62. '''Returns string names of requirements'''
  63. processed_reqs = []
  64. for obj in mesonlib.unholder(mesonlib.listify(reqs)):
  65. if not isinstance(obj, str):
  66. FeatureNew('pkgconfig.generate requirement from non-string object', '0.46.0').use(self.state.subproject)
  67. if hasattr(obj, 'generated_pc'):
  68. self._check_generated_pc_deprecation(obj)
  69. processed_reqs.append(obj.generated_pc)
  70. elif hasattr(obj, 'pcdep'):
  71. pcdeps = mesonlib.listify(obj.pcdep)
  72. for d in pcdeps:
  73. processed_reqs.append(d.name)
  74. self.add_version_reqs(d.name, obj.version_reqs)
  75. elif isinstance(obj, dependencies.PkgConfigDependency):
  76. if obj.found():
  77. processed_reqs.append(obj.name)
  78. self.add_version_reqs(obj.name, obj.version_reqs)
  79. elif isinstance(obj, str):
  80. name, version_req = self.split_version_req(obj)
  81. processed_reqs.append(name)
  82. self.add_version_reqs(name, version_req)
  83. elif isinstance(obj, dependencies.Dependency) and not obj.found():
  84. pass
  85. elif isinstance(obj, ThreadDependency):
  86. pass
  87. else:
  88. raise mesonlib.MesonException('requires argument not a string, '
  89. 'library with pkgconfig-generated file '
  90. 'or pkgconfig-dependency object, '
  91. 'got {!r}'.format(obj))
  92. return processed_reqs
  93. def add_cflags(self, cflags):
  94. self.cflags += mesonlib.stringlistify(cflags)
  95. def _process_libs(self, libs, public):
  96. libs = mesonlib.unholder(mesonlib.listify(libs))
  97. processed_libs = []
  98. processed_reqs = []
  99. processed_cflags = []
  100. for obj in libs:
  101. shared_library_only = getattr(obj, 'shared_library_only', False)
  102. if hasattr(obj, 'pcdep'):
  103. pcdeps = mesonlib.listify(obj.pcdep)
  104. for d in pcdeps:
  105. processed_reqs.append(d.name)
  106. self.add_version_reqs(d.name, obj.version_reqs)
  107. elif hasattr(obj, 'generated_pc'):
  108. self._check_generated_pc_deprecation(obj)
  109. processed_reqs.append(obj.generated_pc)
  110. elif isinstance(obj, dependencies.PkgConfigDependency):
  111. if obj.found():
  112. processed_reqs.append(obj.name)
  113. self.add_version_reqs(obj.name, obj.version_reqs)
  114. elif isinstance(obj, dependencies.InternalDependency):
  115. if obj.found():
  116. processed_libs += obj.get_link_args()
  117. processed_cflags += obj.get_compile_args()
  118. if public:
  119. self.add_pub_libs(obj.libraries)
  120. else:
  121. self.add_priv_libs(obj.libraries)
  122. elif isinstance(obj, dependencies.Dependency):
  123. if obj.found():
  124. processed_libs += obj.get_link_args()
  125. processed_cflags += obj.get_compile_args()
  126. elif isinstance(obj, build.SharedLibrary) and shared_library_only:
  127. # Do not pull dependencies for shared libraries because they are
  128. # only required for static linking. Adding private requires has
  129. # the side effect of exposing their cflags, which is the
  130. # intended behaviour of pkg-config but force Debian to add more
  131. # than needed build deps.
  132. # See https://bugs.freedesktop.org/show_bug.cgi?id=105572
  133. processed_libs.append(obj)
  134. elif isinstance(obj, (build.SharedLibrary, build.StaticLibrary)):
  135. processed_libs.append(obj)
  136. if isinstance(obj, build.StaticLibrary) and public:
  137. self.add_pub_libs(obj.get_dependencies(for_pkgconfig=True))
  138. self.add_pub_libs(obj.get_external_deps())
  139. else:
  140. self.add_priv_libs(obj.get_dependencies(for_pkgconfig=True))
  141. self.add_priv_libs(obj.get_external_deps())
  142. elif isinstance(obj, str):
  143. processed_libs.append(obj)
  144. else:
  145. raise mesonlib.MesonException('library argument not a string, library or dependency object.')
  146. return processed_libs, processed_reqs, processed_cflags
  147. def add_version_reqs(self, name, version_reqs):
  148. if version_reqs:
  149. if name not in self.version_reqs:
  150. self.version_reqs[name] = set()
  151. # Note that pkg-config is picky about whitespace.
  152. # 'foo > 1.2' is ok but 'foo>1.2' is not.
  153. # foo, bar' is ok, but 'foo,bar' is not.
  154. new_vreqs = [s for s in mesonlib.stringlistify(version_reqs)]
  155. self.version_reqs[name].update(new_vreqs)
  156. def split_version_req(self, s):
  157. for op in ['>=', '<=', '!=', '==', '=', '>', '<']:
  158. pos = s.find(op)
  159. if pos > 0:
  160. return s[0:pos].strip(), s[pos:].strip()
  161. return s, None
  162. def format_vreq(self, vreq):
  163. # vreq are '>=1.0' and pkgconfig wants '>= 1.0'
  164. for op in ['>=', '<=', '!=', '==', '=', '>', '<']:
  165. if vreq.startswith(op):
  166. return op + ' ' + vreq[len(op):]
  167. return vreq
  168. def format_reqs(self, reqs):
  169. result = []
  170. for name in reqs:
  171. vreqs = self.version_reqs.get(name, None)
  172. if vreqs:
  173. result += [name + ' ' + self.format_vreq(vreq) for vreq in vreqs]
  174. else:
  175. result += [name]
  176. return ', '.join(result)
  177. def remove_dups(self):
  178. def _fn(xs, libs=False):
  179. # Remove duplicates whilst preserving original order
  180. result = []
  181. for x in xs:
  182. # Don't de-dup unknown strings to avoid messing up arguments like:
  183. # ['-framework', 'CoreAudio', '-framework', 'CoreMedia']
  184. known_flags = ['-pthread']
  185. cannot_dedup = libs and isinstance(x, str) and \
  186. not x.startswith(('-l', '-L')) and \
  187. x not in known_flags
  188. if x not in result or cannot_dedup:
  189. result.append(x)
  190. return result
  191. self.pub_libs = _fn(self.pub_libs, True)
  192. self.pub_reqs = _fn(self.pub_reqs)
  193. self.priv_libs = _fn(self.priv_libs, True)
  194. self.priv_reqs = _fn(self.priv_reqs)
  195. self.cflags = _fn(self.cflags)
  196. # Remove from private libs/reqs if they are in public already
  197. self.priv_libs = [i for i in self.priv_libs if i not in self.pub_libs]
  198. self.priv_reqs = [i for i in self.priv_reqs if i not in self.pub_reqs]
  199. class PkgConfigModule(ExtensionModule):
  200. def _get_lname(self, l, msg, pcfile):
  201. # Nothing special
  202. if not l.name_prefix_set:
  203. return l.name
  204. # Sometimes people want the library to start with 'lib' everywhere,
  205. # which is achieved by setting name_prefix to '' and the target name to
  206. # 'libfoo'. In that case, try to get the pkg-config '-lfoo' arg correct.
  207. if l.prefix == '' and l.name.startswith('lib'):
  208. return l.name[3:]
  209. # If the library is imported via an import library which is always
  210. # named after the target name, '-lfoo' is correct.
  211. if isinstance(l, build.SharedLibrary) and l.import_filename:
  212. return l.name
  213. # In other cases, we can't guarantee that the compiler will be able to
  214. # find the library via '-lfoo', so tell the user that.
  215. mlog.warning(msg.format(l.name, 'name_prefix', l.name, pcfile))
  216. return l.name
  217. def _escape(self, value):
  218. '''
  219. We cannot use quote_arg because it quotes with ' and " which does not
  220. work with pkg-config and pkgconf at all.
  221. '''
  222. # We should always write out paths with / because pkg-config requires
  223. # spaces to be quoted with \ and that messes up on Windows:
  224. # https://bugs.freedesktop.org/show_bug.cgi?id=103203
  225. if isinstance(value, PurePath):
  226. value = value.as_posix()
  227. return value.replace(' ', r'\ ')
  228. def _make_relative(self, prefix, subdir):
  229. if isinstance(prefix, PurePath):
  230. prefix = prefix.as_posix()
  231. if isinstance(subdir, PurePath):
  232. subdir = subdir.as_posix()
  233. try:
  234. if os.path.commonpath([prefix, subdir]) == prefix:
  235. skip = len(prefix) + 1
  236. subdir = subdir[skip:]
  237. except ValueError:
  238. pass
  239. return subdir
  240. def generate_pkgconfig_file(self, state, deps, subdirs, name, description,
  241. url, version, pcfile, conflicts, variables,
  242. uninstalled=False, dataonly=False):
  243. deps.remove_dups()
  244. coredata = state.environment.get_coredata()
  245. if uninstalled:
  246. outdir = os.path.join(state.environment.build_dir, 'meson-uninstalled')
  247. if not os.path.exists(outdir):
  248. os.mkdir(outdir)
  249. prefix = PurePath(state.environment.get_build_dir())
  250. srcdir = PurePath(state.environment.get_source_dir())
  251. else:
  252. outdir = state.environment.scratch_dir
  253. prefix = PurePath(coredata.get_builtin_option('prefix'))
  254. # These always return paths relative to prefix
  255. libdir = PurePath(coredata.get_builtin_option('libdir'))
  256. incdir = PurePath(coredata.get_builtin_option('includedir'))
  257. fname = os.path.join(outdir, pcfile)
  258. with open(fname, 'w', encoding='utf-8') as ofile:
  259. if not dataonly:
  260. ofile.write('prefix={}\n'.format(self._escape(prefix)))
  261. if uninstalled:
  262. ofile.write('srcdir={}\n'.format(self._escape(srcdir)))
  263. else:
  264. ofile.write('libdir={}\n'.format(self._escape('${prefix}' / libdir)))
  265. ofile.write('includedir={}\n'.format(self._escape('${prefix}' / incdir)))
  266. if variables:
  267. ofile.write('\n')
  268. for k, v in variables:
  269. ofile.write('{}={}\n'.format(k, self._escape(v)))
  270. ofile.write('\n')
  271. ofile.write('Name: %s\n' % name)
  272. if len(description) > 0:
  273. ofile.write('Description: %s\n' % description)
  274. if len(url) > 0:
  275. ofile.write('URL: %s\n' % url)
  276. ofile.write('Version: %s\n' % version)
  277. reqs_str = deps.format_reqs(deps.pub_reqs)
  278. if len(reqs_str) > 0:
  279. ofile.write('Requires: {}\n'.format(reqs_str))
  280. reqs_str = deps.format_reqs(deps.priv_reqs)
  281. if len(reqs_str) > 0:
  282. ofile.write('Requires.private: {}\n'.format(reqs_str))
  283. if len(conflicts) > 0:
  284. ofile.write('Conflicts: {}\n'.format(' '.join(conflicts)))
  285. def generate_libs_flags(libs):
  286. msg = 'Library target {0!r} has {1!r} set. Compilers ' \
  287. 'may not find it from its \'-l{2}\' linker flag in the ' \
  288. '{3!r} pkg-config file.'
  289. Lflags = []
  290. for l in libs:
  291. if isinstance(l, str):
  292. yield l
  293. else:
  294. if uninstalled:
  295. install_dir = os.path.dirname(state.backend.get_target_filename_abs(l))
  296. else:
  297. install_dir = l.get_custom_install_dir()[0]
  298. if install_dir is False:
  299. continue
  300. if 'cs' in l.compilers:
  301. if isinstance(install_dir, str):
  302. Lflag = '-r${prefix}/%s/%s' % (self._escape(self._make_relative(prefix, install_dir)), l.filename)
  303. else: # install_dir is True
  304. Lflag = '-r${libdir}/%s' % l.filename
  305. else:
  306. if isinstance(install_dir, str):
  307. Lflag = '-L${prefix}/%s' % self._escape(self._make_relative(prefix, install_dir))
  308. else: # install_dir is True
  309. Lflag = '-L${libdir}'
  310. if Lflag not in Lflags:
  311. Lflags.append(Lflag)
  312. yield Lflag
  313. lname = self._get_lname(l, msg, pcfile)
  314. # If using a custom suffix, the compiler may not be able to
  315. # find the library
  316. if l.name_suffix_set:
  317. mlog.warning(msg.format(l.name, 'name_suffix', lname, pcfile))
  318. if 'cs' not in l.compilers:
  319. yield '-l%s' % lname
  320. def get_uninstalled_include_dirs(libs):
  321. result = []
  322. for l in libs:
  323. if isinstance(l, str):
  324. continue
  325. if l.get_subdir() not in result:
  326. result.append(l.get_subdir())
  327. for i in l.get_include_dirs():
  328. curdir = i.get_curdir()
  329. for d in i.get_incdirs():
  330. path = os.path.join(curdir, d)
  331. if path not in result:
  332. result.append(path)
  333. return result
  334. def generate_uninstalled_cflags(libs):
  335. for d in get_uninstalled_include_dirs(libs):
  336. for basedir in ['${prefix}', '${srcdir}']:
  337. path = os.path.join(basedir, d)
  338. yield '-I%s' % self._escape(path)
  339. if len(deps.pub_libs) > 0:
  340. ofile.write('Libs: {}\n'.format(' '.join(generate_libs_flags(deps.pub_libs))))
  341. if len(deps.priv_libs) > 0:
  342. ofile.write('Libs.private: {}\n'.format(' '.join(generate_libs_flags(deps.priv_libs))))
  343. def generate_compiler_flags():
  344. cflags_buf = []
  345. for f in deps.cflags:
  346. cflags_buf.append(self._escape(f))
  347. return cflags_buf
  348. cflags = generate_compiler_flags()
  349. ofile.write('Cflags:')
  350. if uninstalled:
  351. ofile.write(' '.join(generate_uninstalled_cflags(deps.pub_libs + deps.priv_libs)))
  352. elif not dataonly and cflags:
  353. ofile.write('{}\n'.format(' '.join(cflags)))
  354. @FeatureNewKwargs('pkgconfig.generate', '0.54.0', ['uninstalled_variables'])
  355. @FeatureNewKwargs('pkgconfig.generate', '0.42.0', ['extra_cflags'])
  356. @FeatureNewKwargs('pkgconfig.generate', '0.41.0', ['variables'])
  357. @FeatureNewKwargs('pkgconfig.generate', '0.54.0', ['dataonly'])
  358. @permittedKwargs({'libraries', 'version', 'name', 'description', 'filebase',
  359. 'subdirs', 'requires', 'requires_private', 'libraries_private',
  360. 'install_dir', 'extra_cflags', 'variables', 'url', 'd_module_versions',
  361. 'dataonly', 'conflicts'})
  362. def generate(self, state, args, kwargs):
  363. if 'variables' in kwargs:
  364. FeatureNew('custom pkgconfig variables', '0.41.0').use(state.subproject)
  365. default_version = state.project_version['version']
  366. default_install_dir = None
  367. default_description = None
  368. default_name = None
  369. mainlib = None
  370. default_subdirs = ['.']
  371. if not args and 'version' not in kwargs:
  372. FeatureNew('pkgconfig.generate implicit version keyword', '0.46.0').use(state.subproject)
  373. elif len(args) == 1:
  374. FeatureNew('pkgconfig.generate optional positional argument', '0.46.0').use(state.subproject)
  375. mainlib = getattr(args[0], 'held_object', args[0])
  376. if not isinstance(mainlib, (build.StaticLibrary, build.SharedLibrary)):
  377. raise mesonlib.MesonException('Pkgconfig_gen first positional argument must be a library object')
  378. default_name = mainlib.name
  379. default_description = state.project_name + ': ' + mainlib.name
  380. install_dir = mainlib.get_custom_install_dir()[0]
  381. if isinstance(install_dir, str):
  382. default_install_dir = os.path.join(install_dir, 'pkgconfig')
  383. elif len(args) > 1:
  384. raise mesonlib.MesonException('Too many positional arguments passed to Pkgconfig_gen.')
  385. dataonly = kwargs.get('dataonly', False)
  386. if dataonly:
  387. default_subdirs = []
  388. blocked_vars = ['libraries', 'libraries_private', 'require_private', 'extra_cflags', 'subdirs']
  389. if len(set(kwargs) & set(blocked_vars)) > 0:
  390. raise mesonlib.MesonException('Cannot combine dataonly with any of {}'.format(blocked_vars))
  391. subdirs = mesonlib.stringlistify(kwargs.get('subdirs', default_subdirs))
  392. version = kwargs.get('version', default_version)
  393. if not isinstance(version, str):
  394. raise mesonlib.MesonException('Version must be specified.')
  395. name = kwargs.get('name', default_name)
  396. if not isinstance(name, str):
  397. raise mesonlib.MesonException('Name not specified.')
  398. filebase = kwargs.get('filebase', name)
  399. if not isinstance(filebase, str):
  400. raise mesonlib.MesonException('Filebase must be a string.')
  401. description = kwargs.get('description', default_description)
  402. if not isinstance(description, str):
  403. raise mesonlib.MesonException('Description is not a string.')
  404. url = kwargs.get('url', '')
  405. if not isinstance(url, str):
  406. raise mesonlib.MesonException('URL is not a string.')
  407. conflicts = mesonlib.stringlistify(kwargs.get('conflicts', []))
  408. # Prepend the main library to public libraries list. This is required
  409. # so dep.add_pub_libs() can handle dependency ordering correctly and put
  410. # extra libraries after the main library.
  411. libraries = mesonlib.extract_as_list(kwargs, 'libraries')
  412. if mainlib:
  413. libraries = [mainlib] + libraries
  414. deps = DependenciesHelper(state, filebase)
  415. for d in subdirs:
  416. if d == '.':
  417. deps.add_cflags(['-I${includedir}'])
  418. else:
  419. deps.add_cflags(self._escape(PurePath('-I${includedir}') / d))
  420. deps.add_pub_libs(libraries)
  421. deps.add_priv_libs(kwargs.get('libraries_private', []))
  422. deps.add_pub_reqs(kwargs.get('requires', []))
  423. deps.add_priv_reqs(kwargs.get('requires_private', []))
  424. deps.add_cflags(kwargs.get('extra_cflags', []))
  425. dversions = kwargs.get('d_module_versions', None)
  426. if dversions:
  427. compiler = state.environment.coredata.compilers.host.get('d')
  428. if compiler:
  429. deps.add_cflags(compiler.get_feature_args({'versions': dversions}, None))
  430. def parse_variable_list(stringlist):
  431. reserved = ['prefix', 'libdir', 'includedir']
  432. variables = []
  433. for var in stringlist:
  434. # foo=bar=baz is ('foo', 'bar=baz')
  435. l = var.split('=', 1)
  436. if len(l) < 2:
  437. raise mesonlib.MesonException('Invalid variable "{}". Variables must be in \'name=value\' format'.format(var))
  438. name, value = l[0].strip(), l[1].strip()
  439. if not name or not value:
  440. raise mesonlib.MesonException('Invalid variable "{}". Variables must be in \'name=value\' format'.format(var))
  441. # Variable names must not contain whitespaces
  442. if any(c.isspace() for c in name):
  443. raise mesonlib.MesonException('Invalid whitespace in assignment "{}"'.format(var))
  444. if name in reserved:
  445. raise mesonlib.MesonException('Variable "{}" is reserved'.format(name))
  446. variables.append((name, value))
  447. return variables
  448. variables = parse_variable_list(mesonlib.stringlistify(kwargs.get('variables', [])))
  449. pcfile = filebase + '.pc'
  450. pkgroot = kwargs.get('install_dir', default_install_dir)
  451. if pkgroot is None:
  452. if mesonlib.is_freebsd():
  453. pkgroot = os.path.join(state.environment.coredata.get_builtin_option('prefix'), 'libdata', 'pkgconfig')
  454. else:
  455. pkgroot = os.path.join(state.environment.coredata.get_builtin_option('libdir'), 'pkgconfig')
  456. if not isinstance(pkgroot, str):
  457. raise mesonlib.MesonException('Install_dir must be a string.')
  458. self.generate_pkgconfig_file(state, deps, subdirs, name, description, url,
  459. version, pcfile, conflicts, variables,
  460. False, dataonly)
  461. res = build.Data(mesonlib.File(True, state.environment.get_scratch_dir(), pcfile), pkgroot)
  462. variables = parse_variable_list(mesonlib.stringlistify(kwargs.get('uninstalled_variables', [])))
  463. pcfile = filebase + '-uninstalled.pc'
  464. self.generate_pkgconfig_file(state, deps, subdirs, name, description, url,
  465. version, pcfile, conflicts, variables,
  466. uninstalled=True, dataonly=dataonly)
  467. # Associate the main library with this generated pc file. If the library
  468. # is used in any subsequent call to the generated, it will generate a
  469. # 'Requires:' or 'Requires.private:'.
  470. # Backward compatibility: We used to set 'generated_pc' on all public
  471. # libraries instead of just the main one. Keep doing that but warn if
  472. # anyone is relying on that deprecated behaviour.
  473. if mainlib:
  474. if not hasattr(mainlib, 'generated_pc'):
  475. mainlib.generated_pc = filebase
  476. else:
  477. mlog.warning('Already generated a pkg-config file for', mlog.bold(mainlib.name))
  478. else:
  479. for lib in deps.pub_libs:
  480. if not isinstance(lib, str) and not hasattr(lib, 'generated_pc'):
  481. lib.generated_pc = filebase
  482. location = state.current_node
  483. lib.generated_pc_warn = [name, location]
  484. return ModuleReturnValue(res, [res])
  485. def initialize(*args, **kwargs):
  486. return PkgConfigModule(*args, **kwargs)