2 Copyright 2019 Dmitry Arkhipov
3 Distributed under the Boost Software License, Version 1.0. (See
4 accompanying file LICENSE_1_0.txt or copy at
5 http://www.boost.org/LICENSE_1_0.txt)
25 The *pkg-config* program is used to retrieve information about installed
26 libraries in the system. It retrieves information about packages from special
27 metadata files. These files are named after the package, and have a `.pc`
28 extension. The package name specified to *pkg-config* is defined to be the name
29 of the metadata file, minus the `.pc` extension.
36 == Feature: `pkg-config`
38 Selects one of the initialized `pkg-config` configurations. This feature is
39 `propagated` to dependencies. Its use is dicussed in
40 section <<pkg-config-init>>.
44 feature.feature pkg-config : : propagated ;
49 == Feature: `pkg-config-define`
51 This `free` feature adds a variable assignment to pkg-config invocation. For
56 pkg-config.import mypackage : requirements <pkg-config-define>key=value ;
59 is equivalent to invoking on the comand line
63 pkg-config --define-variable=key=value mypackage ;
68 feature.feature pkg-config-define : : free ;
75 Main target rule that imports a *pkg-config* package. When its consumer targets
76 are built, *pkg-config* command will be invoked with arguments that depend on
77 current property set. The features that have an effect are:
79 * `<pkg-config-define>`: adds a `--define-variable` argument;
80 * `<link>`: adds `--static` argument when `<link>static`;
81 * `<link>`: adds `--static` argument when `<link>static`;
82 * `<name>`: specifies package name (target name is used instead if the property
84 * `<version>`: specifies package version range, can be used multiple times and
85 should be a dot-separated sequence of numbers optionally prefixed with `=`,
86 `<`, `>`, `<=` or `>=`.
92 pkg-config.import my-package
93 : requirements <name>my_package <version><4 <version>>=3.1 ;
104 : usage-requirements *
107 param.handle-named-params
108 sources requirements default-build usage-requirements ;
109 targets.create-metatarget pkg-config-target
110 : [ project.current ]
115 : $(usage-requirements)
122 == Initialization [[ pkg-config-init ]]
124 To use the `pkg-config` tool you need to declare it in a configuration file
125 with the `using` rule:
129 using pkg-config : [config] : [command] ... : [ options ] ... ;
133 * `config`: the name of initialized configuration. The name can be omitted, in
134 which case the configuration will become the default one.
135 * `command`: the command, with any extra arguments, to execute. If no command
136 is given, first `PKG_CONFIG` environment variable is checked, and if its
137 empty the string `pkg-config` is used.
138 * `options`: options that modify `pkg-config` behavior. Allowed options are:
139 * `<path>`: sets `PKG_CONFIG_PATH` environment variable;
140 multiple occurences are allowed.
141 * `<libdir>`: sets `PKG_CONFIG_LIBDIR` environment variable;
142 multiple occurences are allowed.
143 * `<allow-system-cflags>`: sets `PKG_CONFIG_ALLOW_SYSTEM_CFLAGS`
144 environment variable; multiple occurences are allowed.
145 * `<allow-system-libs>`: sets `PKG_CONFIG_ALLOW_SYSTEM_LIBS`
146 environment variable; multiple occurences are allowed.
147 * `<sysroot>`: sets `PKG_CONFIG_SYSROOT_DIR` environment variable;
148 multiple occurences are allowed.
149 * `<variable>`: adds a variable definition argument to command invocation;
150 multiple occurences are allowed.
154 rule init ( config ? : command * : options * )
156 config ?= [ default-config ] ;
158 local tool = [ os.environ PKG_CONFIG ] ;
161 [ common.get-invocation-command pkg-config : $(tool) : $(command) ] ;
163 configure $(config) : $(command) : $(options) ;
164 $(.configs).use $(config) ;
168 rule run ( config ? : args * )
170 config ?= [ default-config ] ;
172 local command = [ $(.configs).get $(config) : command ] ;
173 command = "$(command) $(args:J= )" ;
175 local output = [ SHELL "$(command)" : exit-status ] ;
178 errors.error "pkg-config: command '$(command)' resulted in error:"
179 [ common.newline-char ] $(output[1]) ;
182 local ws = [ string.whitespace ] ;
183 output = [ regex.split $(output[1]) "[$(ws)]" ] ;
184 return [ sequence.filter non-empty : $(output) ] ;
190 == Class `pkg-config-target`
194 class pkg-config-target : alias-target-class {
195 rule construct ( name : sources * : property-set )
196 rule version ( property-set )
197 rule variable ( name : property-set )
201 The class of objects returned by `import` rule. The objects themselves could be
202 useful in situations that require more complicated logic for consuming a
203 package. See <<pkg-config-tips>> for examples.
205 . `rule construct ( name : sources * : property-set )`
206 Overrides `alias-target.construct`.
208 . `rule version ( property-set )`
209 Returns the package's version, in the context of `property-set`.
211 . `rule variable ( name : property-set )`
212 Returns the value of variable `name` in the package, in the context of
218 class pkg-config-target : alias-target-class
223 rule construct ( name : sources * : property-set )
225 local config = [ $(property-set).get <pkg-config> ] ;
226 local args = [ common-arguments $(name) : $(property-set) ] ;
228 [ property-set.create
229 [ compile-flags $(config) $(property-set) : $(args) ]
230 [ link-flags $(config) $(property-set) : $(args) ]
234 rule version ( property-set )
236 local config = [ $(property-set).get <pkg-config> ] ;
237 local args = [ common-arguments [ name ] : $(property-set) ] ;
238 local version = [ pkg-config.run $(config) : --modversion $(args) ] ;
239 return [ regex.split $(version) "\\." ] ;
242 rule variable ( name : property-set )
244 local config = [ $(property-set).get <pkg-config> ] ;
245 local args = [ common-arguments [ name ] : $(property-set) ] ;
246 return [ pkg-config.run $(config) : --variable=$(name) $(args) ] ;
249 local rule common-arguments ( name : property-set )
251 local defines = [ $(property-set).get <pkg-config-define> ] ;
252 local args = --define-variable=$(defines) ;
253 if [ $(property-set).get <link> ] = static
257 return $(args) [ get-package-request $(property-set) $(name) ] ;
260 local rule get-package-request ( property-set name )
262 local pkg-name = [ $(property-set).get <name> ] ;
263 pkg-name ?= $(name) ;
266 errors.error "multiple package names were specified for target "
267 "'$(name)': $(pkg-name)" ;
271 for local version in [ $(property-set).get <version> ]
273 local match = [ MATCH "^(<=)(.*)" : $(version) ] ;
274 match ?= [ MATCH "^(>=)(.*)" : $(version) ] ;
275 match ?= [ MATCH "^([><=])(.*)" : $(version) ] ;
278 version = " $(match:J= )" ;
282 version = " = $(version)" ;
284 versions += $(version) ;
288 return "'$(pkg-name)"$(versions)"'" ;
291 local rule link-flags ( config property-set : args * )
293 local flags = [ pkg-config.run $(config) : --libs $(args) ] ;
294 return <linkflags>$(flags) ;
297 local rule compile-flags ( config property-set : args * )
299 local flags = [ pkg-config.run $(config) : --cflags $(args) ] ;
300 return <cflags>$(flags) ;
305 local rule default-config ( )
311 local rule configure ( config : command + : options * )
313 $(.configs).register $(config) ;
317 local allow-system-cflags ;
318 local allow-system-libs ;
321 for local opt in $(options)
325 case <path> : path += $(opt:G=) ;
326 case <libdir> : libdir += $(opt:G=) ;
327 case <allow-system-cflags> : allow-system-cflags += $(opt:G=) ;
328 case <allow-system-libs> : allow-system-libs += $(opt:G=) ;
329 case <sysroot> : sysroot += $(opt:G=) ;
330 case <variable> : defines += $(opt:G=) ;
332 errors.error "pkg-config: invalid property '$(opt)' was "
333 "specified for configuration '$(config)'." ;
337 for local opt in allow-system-cflags allow-system-libs
339 if ! $($(opt)) in "on" off
341 errors.error "pkg-config: invalid value '$($(opt))' was specified "
342 "for option <$(opt)> of configuration '$(config)'."
343 [ common.newline-char ] "Available values are 'on' and 'off'" ;
349 errors.error "pkg-config: several values were specified for option "
350 "<sysroot> of configuration '$(config)'."
351 [ common.newline-char ] "Only one value is allowed." ;
354 local sep = [ os.path-separator ] ;
355 path = [ envar-set-command PKG_CONFIG_PATH : $(path:J=$(sep)) ] ;
356 libdir = [ envar-set-command PKG_CONFIG_LIBDIR : $(libdir:J=$(sep)) ] ;
357 sysroot = [ envar-set-command PKG_CONFIG_SYSROOT_DIR : $(sysroot) ] ;
359 [ envar-set-command PKG_CONFIG_ALLOW_SYSTEM_CFLAGS
364 [ envar-set-command PKG_CONFIG_ALLOW_SYSTEM_LIBS
369 command += --print-errors --errors-to-stdout --define-variable=$(defines) ;
370 $(.configs).set $(config)
372 : "$(path)$(libdir)$(sysroot)$(allow-cflags)$(allow-libs)$(command:J= )"
375 feature.extend pkg-config : $(config) ;
379 local rule envar-set-command ( envar : value * : implied-value * )
385 value = $(implied-value) ;
387 return [ common.path-variable-setting-command $(envar) : $(value) ] ;
396 local rule non-empty ( string )
398 if $(string) != "" { return true ; }
402 .configs = [ new configurations ] ;
407 == Tips [[pkg-config-tips]]
410 === Using several configurations
412 Suppose, you have 2 collections of `.pc` files: one for platform A, and another
413 for platform B. You can initialize 2 configurations of `pkg-config` tool each
414 corresponding to specific collection:
418 using pkg-config : A : : <libdir>path/to/collection/A ;
419 using pkg-config : B : : <libdir>path/to/collection/B ;
422 Then, you can specify that builds for platform A should use configuration A,
423 while builds for B should use configuration B:
429 <target-os>A-os,<architecture>A-arch:<pkg-config>A
430 <target-os>B-os,<architecture>B-arch:<pkg-config>B
434 Thanks to the fact, that `project-config`, `user-config` and `site-config`
435 modules are parents of jamroot module, you can put it in any of those files.o
438 === Choosing the package name based on the property set
440 Since a file for a package should be named after the package suffixed with
441 `.pc`, some projects came up with naming schemes in order to allow simultaneous
442 installation of several major versions or build variants. In order to pick the
443 specific name corresponding to the build request you can use `<conditional>`
444 property in requirements:
448 pkg-config.import mypackage : requirements <conditional>@infer-name ;
450 rule infer-name ( properties * )
452 local name = mypackage ;
453 local variant = [ property.select <variant> : $(properties) ] ;
454 if $(variant) = debug
458 return <name>$(name) ;
462 The `common.format-name` rule can be very useful in this situation.
465 === Modify usage requirements based on package version or variable
467 Sometimes you need to apply some logic based on package's version or a
468 variable that it defines. For that you can use `<conditional>` property in
473 [ pkg-config.import mypackage : usage-requirements <conditional>@define_ns
476 rule extra-props ( properties * )
478 local ps = [ property-set.create $(properties) ] ;
479 local prefix = [ $(mypackage).variable name_prefix : $(ps) ] ;
480 prefix += [ $(mypackage).version $(ps) ] ;
481 return <define>$(prefix:J=_) ;