mirror of
https://git.rtems.org/rtems-source-builder
synced 2024-10-09 07:15:10 +08:00
sb: Add pkg-config support.
Add a pkg-config look alike command so packages that use pkg-config can build if pkg-config is not present on a host. Add support to query package config from configuration scripts.
This commit is contained in:
parent
56e8d80055
commit
c4fefdeb55
@ -1,356 +0,0 @@
|
||||
#! /usr/bin/env python
|
||||
#
|
||||
# RTEMS Tools Project (http://www.rtems.org/)
|
||||
# Copyright 2014 Chris Johns (chrisj@rtems.org)
|
||||
# All rights reserved.
|
||||
#
|
||||
# This file is part of the RTEMS Tools package in 'rtems-tools'.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
#
|
||||
# Pkg-config in python. It attempts to provide a few simple features
|
||||
# provided by the full pkg-config so packages can configure and build.
|
||||
#
|
||||
# Pkg-config as a tool is a good idea how-ever the implementation creates a
|
||||
# range of problems. If it was implemented with all parts included it would be
|
||||
# portable and I suspect useful to others on platforms other than Linux and
|
||||
# Unix equivs that come with packaging systems..
|
||||
#
|
||||
# Note: This is used on platforms where pkg-config is not easy to get
|
||||
# working.
|
||||
#
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
import sys
|
||||
|
||||
#
|
||||
# Make trace true to get a file of what happens and what is being asked.
|
||||
#
|
||||
trace = True
|
||||
logfile = 'pc.log'
|
||||
out = None
|
||||
|
||||
pkg_aliases = {
|
||||
# Use to add package name aliases eg
|
||||
# 'package': ['pkg_alias'],
|
||||
}
|
||||
|
||||
def log(s, lf = True):
|
||||
global trace, logfile, out
|
||||
if trace:
|
||||
if out is None:
|
||||
if logfile:
|
||||
out = open(logfile, 'a')
|
||||
else:
|
||||
out = sys.stdout
|
||||
if lf:
|
||||
print >> out, s
|
||||
else:
|
||||
print >> out, s,
|
||||
|
||||
def default_prefix():
|
||||
paths = ['/usr', '/usr/local']
|
||||
if 'PKG_CONFIG_PATH' in os.environ:
|
||||
paths += os.environ['PKG_CONFIG_PATH'].split(':')
|
||||
return paths
|
||||
|
||||
class error(Exception):
|
||||
def __init__(self, msg):
|
||||
self.msg = msg
|
||||
|
||||
def __str__(self):
|
||||
return self.msg
|
||||
|
||||
class package:
|
||||
def __init__(self, file = None):
|
||||
self.defines = {}
|
||||
self.fields = {}
|
||||
if file:
|
||||
self.load(file)
|
||||
|
||||
def __str__(self):
|
||||
s = ''
|
||||
for d in sorted(self.defines):
|
||||
s += 'd: %s: %s%s' % (d, self.defines[d], os.linesep)
|
||||
for f in sorted(self.fields):
|
||||
s += 'f: %s: %s%s' % (f, self.fields[f], os.linesep)
|
||||
return s
|
||||
|
||||
def _sysroot(self, s):
|
||||
if 'PKG_CONFIG_SYSROOT_DIR' in os.environ:
|
||||
sysroot = os.environ['PKG_CONFIG_SYSROOT_DIR']
|
||||
offset = 0
|
||||
while True:
|
||||
dash = s[offset:].find('-')
|
||||
if dash < 0:
|
||||
break
|
||||
if offset + dash + 2 < len(s) and s[offset + dash + 1] in 'LI':
|
||||
s = s[:offset + dash + 2] + sysroot + s[offset + dash + 2:]
|
||||
offset += dash + 1
|
||||
return s
|
||||
|
||||
def load(self, file):
|
||||
f = open(file)
|
||||
tm = False
|
||||
for l in f.readlines():
|
||||
l = l[:-1]
|
||||
hash = l.find('#')
|
||||
if hash >= 0:
|
||||
l = l[:hash]
|
||||
if len(l):
|
||||
d = 0
|
||||
define = False
|
||||
eq = l.find('=')
|
||||
dd = l.find(':')
|
||||
if eq > 0 and dd > 0:
|
||||
if eq < dd:
|
||||
define = True
|
||||
d = eq
|
||||
else:
|
||||
define = False
|
||||
d = dd
|
||||
elif eq >= 0:
|
||||
define = True
|
||||
d = eq
|
||||
elif dd >= 0:
|
||||
define = False
|
||||
d = dd
|
||||
if d > 0:
|
||||
lhs = l[:d].lower()
|
||||
rhs = l[d + 1:]
|
||||
|
||||
if tm:
|
||||
print('define: ' + str(define) + ', lhs: ' + lhs + ', ' + rhs)
|
||||
|
||||
if define:
|
||||
self.defines[lhs] = rhs
|
||||
else:
|
||||
self.fields[lhs] = rhs
|
||||
|
||||
def get(self, label):
|
||||
if label.lower() not in self.fields:
|
||||
None
|
||||
mre = re.compile('\$\{[^\}]+\}')
|
||||
s = self.fields[label.lower()]
|
||||
expanded = True
|
||||
tm = False
|
||||
while expanded:
|
||||
expanded = False
|
||||
if tm:
|
||||
log('pc:get: "' + s + '"')
|
||||
ms = mre.findall(s)
|
||||
for m in ms:
|
||||
mn = m[2:-1]
|
||||
if mn.lower() in self.defines:
|
||||
s = s.replace(m, self.defines[mn.lower()])
|
||||
expanded = True
|
||||
return self._sysroot(s)
|
||||
|
||||
def _check_package(lib_check, args, pc_paths):
|
||||
ec = 1
|
||||
pkg = None
|
||||
ls = lib_check.split()
|
||||
lib = ls[0]
|
||||
if len(pc_paths):
|
||||
for path in pc_paths:
|
||||
pc = os.path.join(path, '%s.pc' % (lib))
|
||||
if os.path.isfile(pc):
|
||||
pkg = package(file = pc)
|
||||
if args.dump:
|
||||
log(pkg)
|
||||
ec = 0
|
||||
break
|
||||
if ec > 0:
|
||||
versions = []
|
||||
for prefix in args.prefix:
|
||||
prefix = os.path.join(prefix, 'lib')
|
||||
if os.path.exists(prefix):
|
||||
for l in os.listdir(prefix):
|
||||
if l.startswith(lib + '.'):
|
||||
versions += [l]
|
||||
break
|
||||
if len(versions) > 0:
|
||||
if len(ls) > 1:
|
||||
ec = 0
|
||||
elif args.exists:
|
||||
ec = 0
|
||||
elif args.exact_version:
|
||||
ec = 0
|
||||
elif args.atleast_version:
|
||||
ec = 0
|
||||
elif args.atleast_version:
|
||||
ec = 0
|
||||
return ec, pkg
|
||||
|
||||
def run(argv):
|
||||
|
||||
class version_action(argparse.Action):
|
||||
def __call__(self, parser, namespace, values, option_string = None):
|
||||
parts = values.strip().split('.')
|
||||
for p in parts:
|
||||
if not p.isdigit():
|
||||
raise error('invalid version value: %s' % (values))
|
||||
setattr(namespace, self.dest, '.'.join(parts))
|
||||
|
||||
ec = 0
|
||||
|
||||
opts = argparse.ArgumentParser(prog = 'pkg-config',
|
||||
description = 'Package Configuration.')
|
||||
opts.add_argument('libraries', metavar='lib', type = str, nargs = '+',
|
||||
help = 'a library')
|
||||
opts.add_argument('--modversion', dest = 'modversion', action = 'store', default = None,
|
||||
help = 'Requests that the version information of the libraries.')
|
||||
opts.add_argument('--print-errors', dest = 'print_errors', action = 'store_true',
|
||||
default = False,
|
||||
help = 'Print any errors.')
|
||||
opts.add_argument('--short-errors', dest = 'short_errors', action = 'store_true',
|
||||
default = False,
|
||||
help = 'Make error messages short.')
|
||||
opts.add_argument('--silence-errors', dest = 'silence_errors', action = 'store_true',
|
||||
default = False,
|
||||
help = 'Do not print any errors.')
|
||||
opts.add_argument('--errors-to-stdout', dest = 'errors_to_stdout', action = 'store_true',
|
||||
default = False,
|
||||
help = 'Print errors to stdout rather than stderr.')
|
||||
opts.add_argument('--cflags', dest = 'cflags', action = 'store_true',
|
||||
default = False,
|
||||
help = 'This prints pre-processor and compile flags required to' \
|
||||
' compile the package(s)')
|
||||
opts.add_argument('--libs', dest = 'libs', action = 'store_true',
|
||||
default = False,
|
||||
help = 'This option is identical to "--cflags", only it prints the' \
|
||||
' link flags.')
|
||||
opts.add_argument('--libs-only-L', dest = 'libs_only_L', action = 'store_true',
|
||||
default = False,
|
||||
help = 'This prints the -L/-R part of "--libs".')
|
||||
opts.add_argument('--libs-only-l', dest = 'libs_only_l', action = 'store_true',
|
||||
default = False,
|
||||
help = 'This prints the -l part of "--libs".')
|
||||
opts.add_argument('--variable', dest = 'variable', action = 'store',
|
||||
default = None,
|
||||
help = 'This returns the value of a variable.')
|
||||
opts.add_argument('--define-variable', dest = 'define_variable', action = 'store',
|
||||
default = None,
|
||||
help = 'This sets a global value for a variable')
|
||||
opts.add_argument('--uninstalled', dest = 'uninstalled', action = 'store_true',
|
||||
default = False,
|
||||
help = 'Ignored')
|
||||
opts.add_argument('--atleast-pkgconfig-version', dest = 'atleast_pkgconfig_version', action = 'store_true',
|
||||
default = False,
|
||||
help = 'Check the version of package config. Always ok.')
|
||||
opts.add_argument('--exists', dest = 'exists', action = 'store_true',
|
||||
default = False,
|
||||
help = 'Test if a library is present')
|
||||
opts.add_argument('--atleast-version', dest = 'atleast_version', action = version_action,
|
||||
default = None,
|
||||
help = 'The package is at least this version.')
|
||||
opts.add_argument('--exact-version', dest = 'exact_version', action = version_action,
|
||||
default = None,
|
||||
help = 'The package is the exact version.')
|
||||
opts.add_argument('--max-version', dest = 'max_version', action = version_action,
|
||||
default = None,
|
||||
help = 'The package is no later than this version.')
|
||||
opts.add_argument('--msvc-syntax', dest = 'msvc_syntax', action = 'store_true',
|
||||
default = False,
|
||||
help = 'Ignored')
|
||||
opts.add_argument('--dont-define-prefix', dest = 'dont_define_prefix', action = 'store_true',
|
||||
default = False,
|
||||
help = 'Ignored')
|
||||
opts.add_argument('--prefix-variable', dest = 'prefix', action = 'store',
|
||||
default = default_prefix(),
|
||||
help = 'Define the prefix.')
|
||||
opts.add_argument('--static', dest = 'static', action = 'store_true',
|
||||
default = False,
|
||||
help = 'Output libraries suitable for static linking')
|
||||
opts.add_argument('--dump', dest = 'dump', action = 'store_true',
|
||||
default = False,
|
||||
help = 'Dump the package if one is found.')
|
||||
|
||||
args = opts.parse_args(argv[1:])
|
||||
|
||||
if (args.exists and (args.exact_version or args.max_version)) or \
|
||||
(args.exact_version and (args.exists or args.max_version)) or \
|
||||
(args.max_version and (args.exists or args.exact_version)):
|
||||
raise error('only one of --exists, --exact-version, or --max-version')
|
||||
|
||||
pc_paths = []
|
||||
|
||||
if args.prefix:
|
||||
for prefix in args.prefix:
|
||||
prefix = os.path.join(prefix, 'lib', 'pkgconfig')
|
||||
if os.path.exists(prefix):
|
||||
pc_paths += [prefix]
|
||||
|
||||
if len(pc_paths):
|
||||
log('pc_paths = %s' % (', '.join(pc_paths)))
|
||||
else:
|
||||
log('pc_paths = None')
|
||||
|
||||
exists = False
|
||||
|
||||
ec = 1
|
||||
|
||||
if args.atleast_pkgconfig_version:
|
||||
ec = 0
|
||||
else:
|
||||
for lib in args.libraries:
|
||||
ec, pkg = _check_package(lib, args, pc_paths)
|
||||
if ec > 0:
|
||||
for pa in pkg_aliases:
|
||||
if lib.startswith(pa):
|
||||
for a in pkg_aliases[pa]:
|
||||
ec, pkg = _check_package(a, args, pc_paths)
|
||||
if ec == 0:
|
||||
break
|
||||
if ec == 0:
|
||||
if pkg:
|
||||
if args.cflags:
|
||||
cflags = pkg.get('cflags')
|
||||
print cflags
|
||||
log('cflags: %s' % (cflags))
|
||||
if args.libs:
|
||||
libs = pkg.get('libs')
|
||||
print libs
|
||||
log('libs: %s' % (libs))
|
||||
return ec
|
||||
|
||||
try:
|
||||
log('-' * 40)
|
||||
log('pkg-config', lf = False)
|
||||
for a in sys.argv[1:]:
|
||||
log(' "%s"' % (a), lf = False)
|
||||
log('')
|
||||
ec = run(sys.argv)
|
||||
log('ec = %d' % (ec))
|
||||
except ImportError:
|
||||
print >> sys.stderr, "incorrect package config installation"
|
||||
sys.exit(1)
|
||||
except error, e:
|
||||
print >> sys.stderr, 'error: %s' % (e)
|
||||
sys.exit(1)
|
||||
sys.exit(ec)
|
@ -58,7 +58,7 @@ optflags: none, convert, '-O2 -pipe'
|
||||
optincludes: none, convert, ''
|
||||
|
||||
# Extra path a platform can override.
|
||||
_extra_path: none, none, ''
|
||||
_extra_path: none, none, '%{_sbdir}'
|
||||
_ld_library_path: none, none, 'LD_LIBRARY_PATH'
|
||||
|
||||
# Paths
|
||||
|
244
source-builder/pkg-config
Executable file
244
source-builder/pkg-config
Executable file
@ -0,0 +1,244 @@
|
||||
#! /usr/bin/env python
|
||||
#
|
||||
# RTEMS Tools Project (http://www.rtems.org/)
|
||||
# Copyright 2014 Chris Johns (chrisj@rtems.org)
|
||||
# All rights reserved.
|
||||
#
|
||||
# This file is part of the RTEMS Tools package in 'rtems-tools'.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
base = os.path.dirname(sys.argv[0])
|
||||
sys.path.insert(0, base + '/sb')
|
||||
|
||||
try:
|
||||
import pkgconfig
|
||||
except ImportError:
|
||||
print >> sys.stderr, "Incorrect Source Builder installation"
|
||||
sys.exit(1)
|
||||
|
||||
#
|
||||
# Make trace true to get a file of what happens and what is being asked.
|
||||
#
|
||||
trace = True
|
||||
trace_stdout = False
|
||||
logfile = 'pkg-config.log'
|
||||
out = None
|
||||
|
||||
#
|
||||
# Write all the package source parsed to a single file.
|
||||
#
|
||||
trace_src = True
|
||||
if trace_src:
|
||||
src = open('pkg-src.txt', 'w')
|
||||
|
||||
def log(s, lf = True):
|
||||
global trace, logfile, out
|
||||
if trace:
|
||||
if out is None:
|
||||
if logfile:
|
||||
out = open(logfile, 'a')
|
||||
else:
|
||||
out = sys.stdout
|
||||
if lf:
|
||||
if out != sys.stdout and trace_stdout:
|
||||
print s
|
||||
print >> out, s
|
||||
else:
|
||||
if out != sys.stdout and trace_stdout:
|
||||
print s,
|
||||
print >> out, s,
|
||||
|
||||
def _check_package(lib_check, args):
|
||||
ec = 1
|
||||
pkg = None
|
||||
flags = { 'cflags': '',
|
||||
'libs': '' }
|
||||
libs = pkgconfig.package.splitter(lib_check)
|
||||
for lib in libs:
|
||||
log('pkg: %s' % (lib))
|
||||
pkg = pkgconfig.package(lib[0], prefix = args.prefix, output = log, src = src)
|
||||
if args.dump:
|
||||
log(pkg)
|
||||
if pkg.exists():
|
||||
if len(lib) == 1:
|
||||
if args.exact_version:
|
||||
if pkg.check('=', args.exact_version):
|
||||
ec = 0
|
||||
elif args.atleast_version:
|
||||
if pkg.check('>=', args.atleast_version):
|
||||
ec = 0
|
||||
elif args.max_version:
|
||||
if pkg.check('<=', args.max_version):
|
||||
ec = 0
|
||||
else:
|
||||
ec = 0
|
||||
else:
|
||||
if len(lib) != 3:
|
||||
raise error('invalid package check: %s' % (' '.join(lib)))
|
||||
if pkg.check(lib[1], lib[2]):
|
||||
ec = 0
|
||||
if ec == 0:
|
||||
flags['cflags'] += pkg.get('cflags')
|
||||
flags['libs'] += pkg.get('libs', private = False)
|
||||
break
|
||||
if ec > 0:
|
||||
break
|
||||
return ec, pkg, flags
|
||||
|
||||
def run(argv):
|
||||
|
||||
class version_action(argparse.Action):
|
||||
def __call__(self, parser, namespace, values, option_string = None):
|
||||
parts = values[0].strip().split('.')
|
||||
for p in parts:
|
||||
if not p.isdigit():
|
||||
raise error('invalid version value: %s' % (values))
|
||||
setattr(namespace, self.dest, '.'.join(parts))
|
||||
|
||||
ec = 0
|
||||
|
||||
opts = argparse.ArgumentParser(prog = 'pkg-config', description = 'Package Configuration.')
|
||||
opts.add_argument('libraries', metavar='lib', type = str, help = 'a library', nargs = '*')
|
||||
opts.add_argument('--modversion', dest = 'modversion', action = 'store', default = None,
|
||||
help = 'Requests that the version information of the libraries.')
|
||||
opts.add_argument('--print-errors', dest = 'print_errors', action = 'store_true',
|
||||
default = False,
|
||||
help = 'Print any errors.')
|
||||
opts.add_argument('--short-errors', dest = 'short_errors', action = 'store_true',
|
||||
default = False,
|
||||
help = 'Make error messages short.')
|
||||
opts.add_argument('--silence-errors', dest = 'silence_errors', action = 'store_true',
|
||||
default = False,
|
||||
help = 'Do not print any errors.')
|
||||
opts.add_argument('--errors-to-stdout', dest = 'errors_to_stdout', action = 'store_true',
|
||||
default = False,
|
||||
help = 'Print errors to stdout rather than stderr.')
|
||||
opts.add_argument('--cflags', dest = 'cflags', action = 'store_true',
|
||||
default = False,
|
||||
help = 'This prints pre-processor and compile flags required to' \
|
||||
' compile the package(s)')
|
||||
opts.add_argument('--libs', dest = 'libs', action = 'store_true',
|
||||
default = False,
|
||||
help = 'This option is identical to "--cflags", only it prints the' \
|
||||
' link flags.')
|
||||
opts.add_argument('--libs-only-L', dest = 'libs_only_L', action = 'store_true',
|
||||
default = False,
|
||||
help = 'This prints the -L/-R part of "--libs".')
|
||||
opts.add_argument('--libs-only-l', dest = 'libs_only_l', action = 'store_true',
|
||||
default = False,
|
||||
help = 'This prints the -l part of "--libs".')
|
||||
opts.add_argument('--variable', dest = 'variable', action = 'store',
|
||||
nargs = 1, default = None,
|
||||
help = 'This returns the value of a variable.')
|
||||
opts.add_argument('--define-variable', dest = 'define_variable', action = 'store',
|
||||
nargs = 1, default = None,
|
||||
help = 'This sets a global value for a variable')
|
||||
opts.add_argument('--uninstalled', dest = 'uninstalled', action = 'store_true',
|
||||
default = False,
|
||||
help = 'Ignored')
|
||||
opts.add_argument('--atleast-pkgconfig-version', dest = 'atleast_pkgconfig_version',
|
||||
action = 'store', nargs = 1, default = None,
|
||||
help = 'Check the version of package config. Always ok.')
|
||||
opts.add_argument('--exists', dest = 'exists', action = 'store_true',
|
||||
default = False,
|
||||
help = 'Test if a library is present')
|
||||
opts.add_argument('--atleast-version', dest = 'atleast_version',
|
||||
action = version_action, nargs = 1, default = None,
|
||||
help = 'The package is at least this version.')
|
||||
opts.add_argument('--exact-version', dest = 'exact_version', action = version_action,
|
||||
nargs = 1, default = None,
|
||||
help = 'The package is the exact version.')
|
||||
opts.add_argument('--max-version', dest = 'max_version', action = version_action,
|
||||
nargs = 1, default = None,
|
||||
help = 'The package is no later than this version.')
|
||||
opts.add_argument('--msvc-syntax', dest = 'msvc_syntax', action = 'store_true',
|
||||
default = False,
|
||||
help = 'Ignored')
|
||||
opts.add_argument('--dont-define-prefix', dest = 'dont_define_prefix', action = 'store_true',
|
||||
default = False,
|
||||
help = 'Ignored')
|
||||
opts.add_argument('--prefix-variable', dest = 'prefix', action = 'store',
|
||||
nargs = 1, default = pkgconfig.default_prefix(),
|
||||
help = 'Define the prefix.')
|
||||
opts.add_argument('--static', dest = 'static', action = 'store_true',
|
||||
default = False,
|
||||
help = 'Output libraries suitable for static linking')
|
||||
opts.add_argument('--dump', dest = 'dump', action = 'store_true',
|
||||
default = False,
|
||||
help = 'Dump the package if one is found.')
|
||||
|
||||
args = opts.parse_args(argv[1:])
|
||||
|
||||
if (args.exists and (args.exact_version or args.max_version)) or \
|
||||
(args.exact_version and (args.exists or args.max_version)) or \
|
||||
(args.max_version and (args.exists or args.exact_version)):
|
||||
raise error('only one of --exists, --exact-version, or --max-version')
|
||||
|
||||
exists = False
|
||||
|
||||
ec = 1
|
||||
|
||||
if args.atleast_pkgconfig_version:
|
||||
ec = 0
|
||||
else:
|
||||
for lib in args.libraries:
|
||||
ec, pkg, flags = _check_package(lib, args)
|
||||
if ec == 0:
|
||||
if args.cflags:
|
||||
if len(flags['cflags']):
|
||||
print flags['cflags']
|
||||
log('cflags: %s' % (flags['cflags']))
|
||||
else:
|
||||
log('cflags: empty')
|
||||
if args.libs:
|
||||
if len(flags['libs']):
|
||||
print flags['libs']
|
||||
log('libs: %s' % (flags['libs']))
|
||||
else:
|
||||
log('libs: empty')
|
||||
|
||||
#pkgconfig.package.dump_loaded()
|
||||
|
||||
return ec
|
||||
|
||||
try:
|
||||
log('-' * 40)
|
||||
log('pkg-config', lf = False)
|
||||
for a in sys.argv[1:]:
|
||||
log(' "%s"' % (a), lf = False)
|
||||
log('')
|
||||
ec = run(sys.argv)
|
||||
log('ec = %d' % (ec))
|
||||
except ImportError:
|
||||
print >> sys.stderr, "incorrect package config installation"
|
||||
sys.exit(1)
|
||||
except pkgconfig.error, e:
|
||||
print >> sys.stderr, 'error: %s' % (e)
|
||||
sys.exit(1)
|
||||
sys.exit(ec)
|
@ -36,6 +36,7 @@ try:
|
||||
import log
|
||||
import options
|
||||
import path
|
||||
import pkgconfig
|
||||
except KeyboardInterrupt:
|
||||
print 'user terminated'
|
||||
sys.exit(1)
|
||||
@ -367,6 +368,26 @@ class file:
|
||||
raise error.general('shell macro failed: %s:%d: %s' % (s, exit_code, output))
|
||||
return line
|
||||
|
||||
def _pkgconfig(self, test):
|
||||
ts = test.split()
|
||||
ok = False
|
||||
pkg = pkgconfig.package(ts[0], output = log.output)
|
||||
if len(ts) > 1 and len(ts) != 3:
|
||||
self._error('malformed pkgconfig check')
|
||||
else:
|
||||
op = '>='
|
||||
ver = '0'
|
||||
if len(ts) == 3:
|
||||
op = ts[1]
|
||||
ver = self.macros.expand(ts[2])
|
||||
try:
|
||||
ok = pkg.check(op, ver)
|
||||
except pkgconfig.error, pe:
|
||||
self._error('pkgconfig: %s' % (pe))
|
||||
except:
|
||||
raise error.interal('pkgconfig failure')
|
||||
return ok
|
||||
|
||||
def _expand(self, s):
|
||||
expand_count = 0
|
||||
expanded = True
|
||||
@ -429,6 +450,13 @@ class file:
|
||||
s = s.replace(m, '0')
|
||||
expanded = True
|
||||
mn = None
|
||||
elif m.startswith('%{pkgconfig'):
|
||||
if self._pkgconfig(m[11:-1].strip()):
|
||||
s = s.replace(m, '1')
|
||||
else:
|
||||
s = s.replace(m, '0')
|
||||
expanded = True
|
||||
mn = None
|
||||
elif m.startswith('%{?') or m.startswith('%{!?'):
|
||||
if m[2] == '!':
|
||||
start = 4
|
||||
|
490
source-builder/sb/pkgconfig.py
Executable file
490
source-builder/sb/pkgconfig.py
Executable file
@ -0,0 +1,490 @@
|
||||
#! /usr/bin/env python
|
||||
#
|
||||
# RTEMS Tools Project (http://www.rtems.org/)
|
||||
# Copyright 2014 Chris Johns (chrisj@rtems.org)
|
||||
# All rights reserved.
|
||||
#
|
||||
# This file is part of the RTEMS Tools package in 'rtems-tools'.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
#
|
||||
# Pkg-config in python. It attempts to provide a few simple features
|
||||
# provided by the full pkg-config so packages can configure and build.
|
||||
#
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
import shlex
|
||||
import sys
|
||||
|
||||
def default_prefix():
|
||||
paths = ['/usr', '/usr/local']
|
||||
if 'PKG_CONFIG_PATH' in os.environ:
|
||||
paths += os.environ['PKG_CONFIG_PATH'].split(':')
|
||||
return paths
|
||||
|
||||
class error(Exception):
|
||||
def __init__(self, msg):
|
||||
self.msg = msg
|
||||
|
||||
def __str__(self):
|
||||
return self.msg
|
||||
|
||||
class package(object):
|
||||
|
||||
node_types = ['requires', 'requires.private']
|
||||
node_type_labels = { 'requires': 'r', 'requires.private': 'rp', 'failed': 'F' }
|
||||
version_ops = ['=', '<', '>', '<=', '>=', '!=']
|
||||
config_prefixes = ['lib', 'libdata']
|
||||
get_recursion = ['cflags', 'libs']
|
||||
no_dup_flags = ['-I', '-l', '-L']
|
||||
dual_opts = ['-D', '-U', '-I', '-l', '-L']
|
||||
lib_list_splitter = re.compile('[\s,]+')
|
||||
loaded = {}
|
||||
|
||||
@staticmethod
|
||||
def is_version(v):
|
||||
for n in v.split('.'):
|
||||
if not n.isdigit():
|
||||
return False
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def splitter(pkg_list):
|
||||
pkgs = []
|
||||
pls = package.lib_list_splitter.split(pkg_list)
|
||||
i = 0
|
||||
while i < len(pls):
|
||||
pkg = [pls[i]]
|
||||
i += 1
|
||||
if i < len(pls):
|
||||
op = None
|
||||
if package.is_version(pls[i]):
|
||||
op = '>='
|
||||
ver = pls[i]
|
||||
i += 1
|
||||
elif pls[i] in package.version_ops:
|
||||
op = pls[i]
|
||||
i += 1
|
||||
if i < len(pls):
|
||||
ver = pls[i]
|
||||
i += 1
|
||||
else:
|
||||
op = '>='
|
||||
ver = '0'
|
||||
pkg += [op, ver]
|
||||
else:
|
||||
pkg += ['>=', '0']
|
||||
pkgs += [pkg]
|
||||
return pkgs
|
||||
|
||||
@staticmethod
|
||||
def check_versions(lhs, op, rhs):
|
||||
if op not in package.version_ops:
|
||||
raise error('bad operator: %s' % (op))
|
||||
if not lhs or not rhs:
|
||||
return False
|
||||
slhs = lhs.split('.')
|
||||
srhs = rhs.split('.')
|
||||
ok = True
|
||||
i = 0
|
||||
while i < len(srhs):
|
||||
try:
|
||||
l = int(slhs[i])
|
||||
r = int(srhs[i])
|
||||
except:
|
||||
return False
|
||||
if op == '=':
|
||||
if l != r:
|
||||
ok = False
|
||||
break
|
||||
elif op == '<':
|
||||
if l < r:
|
||||
break
|
||||
if l >= r:
|
||||
ok = False
|
||||
break
|
||||
elif op == '>':
|
||||
if l > r:
|
||||
break
|
||||
if l <= r:
|
||||
ok = False
|
||||
break
|
||||
elif op == '<=':
|
||||
if l < r:
|
||||
break
|
||||
if l > r:
|
||||
ok = False
|
||||
break
|
||||
elif op == '>=':
|
||||
if l > r:
|
||||
break
|
||||
if l < r:
|
||||
ok = False
|
||||
break
|
||||
elif op == '!=':
|
||||
if l != r:
|
||||
ok = True
|
||||
break
|
||||
if l == r:
|
||||
ok = False
|
||||
i += 1
|
||||
return ok
|
||||
|
||||
@staticmethod
|
||||
def dump_loaded():
|
||||
for n in sorted(package.loaded):
|
||||
print package.loaded[n]._str()
|
||||
|
||||
def __init__(self, name = None, prefix = None, output = None, src = None):
|
||||
self._clean()
|
||||
self.name_ = name
|
||||
self.output = output
|
||||
self.src = src
|
||||
self.prefix = None
|
||||
self.paths = []
|
||||
if prefix is None:
|
||||
prefix = default_prefix()
|
||||
if prefix:
|
||||
if type(prefix) is str:
|
||||
self.prefix = prefix.split(os.pathsep)
|
||||
elif type(prefix) is list:
|
||||
self.prefix = prefix
|
||||
else:
|
||||
raise error('invalid type of prefix')
|
||||
for p in self.prefix:
|
||||
for d in package.config_prefixes:
|
||||
prefix = os.path.join(p, d, 'pkgconfig')
|
||||
if os.path.exists(prefix):
|
||||
self.paths += [prefix]
|
||||
self._log('paths: %s' % (', '.join(self.paths)))
|
||||
if 'PKG_CONFIG_SYSROOT_DIR' in os.environ:
|
||||
self.defines['sysroot'] = os.environ['PKG_CONFIG_SYSROOT_DIR']
|
||||
self._log('sysroot: %s' % (self.defines['sysroot']))
|
||||
if 'PKG_CONFIG_BUILD_TOP_DIR' in os.environ:
|
||||
self.defines['top_builddir'] = os.environ['PKG_CONFIG_BUILD_TOP_DIR']
|
||||
self._log('top_builddir: %s' % (self.defines['top_builddir']))
|
||||
if self.name_:
|
||||
self.load(self.name_)
|
||||
|
||||
def __str__(self):
|
||||
s = self._str()
|
||||
for nt in package.node_types:
|
||||
for n in sorted(self.nodes[nt]):
|
||||
s += ' ' + \
|
||||
' '.join(['%s%s' % (s, os.linesep) \
|
||||
for s in str(self.nodes[nt][n]).split(os.linesep)])
|
||||
return s[:-1]
|
||||
|
||||
def _str(self):
|
||||
if self.file_ or len(self.libraries):
|
||||
e = 'E'
|
||||
else:
|
||||
e = '-'
|
||||
s = '> %s %s (%s)%s' % (self.name_, e, self.file_, os.linesep)
|
||||
for d in sorted(self.defines):
|
||||
s += 'd: %s: %s%s' % (d, self.defines[d], os.linesep)
|
||||
for f in sorted(self.fields):
|
||||
s += 'f: %s: %s%s' % (f, self.fields[f], os.linesep)
|
||||
for l in sorted(self.libraries):
|
||||
s += 'l: %s%s' % (l, os.linesep)
|
||||
for nt in package.node_types + ['failed']:
|
||||
s += '%s: ' % (package.node_type_labels[nt])
|
||||
if len(self.nodes[nt]):
|
||||
txt = []
|
||||
for n in sorted(self.nodes[nt]):
|
||||
if self.nodes[nt][n].exists():
|
||||
e = ''
|
||||
else:
|
||||
e = '*'
|
||||
txt += ['%s%s' % (n, e)]
|
||||
s += '%s%s' % (', '.join(txt), os.linesep)
|
||||
else:
|
||||
s += 'none' + os.linesep
|
||||
return s #[:-1]
|
||||
|
||||
def _clean(self):
|
||||
self.name_ = None
|
||||
self.file_ = None
|
||||
self.defines = {}
|
||||
self.fields = {}
|
||||
self.nodes = { 'failed': {} }
|
||||
for nt in package.node_types:
|
||||
self.nodes[nt] = {}
|
||||
self.libraries = []
|
||||
|
||||
def _log(self, s):
|
||||
if self.output:
|
||||
self.output(s)
|
||||
|
||||
def _find_package(self, name):
|
||||
if len(self.paths):
|
||||
for path in self.paths:
|
||||
pc = os.path.join(path, '%s.pc' % (name))
|
||||
if os.path.isfile(pc):
|
||||
return pc;
|
||||
return None
|
||||
|
||||
def _find_libraries(self, name):
|
||||
libraries = []
|
||||
for prefix in self.prefix:
|
||||
prefix = os.path.join(prefix, 'lib')
|
||||
if os.path.exists(prefix):
|
||||
for l in os.listdir(prefix):
|
||||
if l.startswith(name + '.'):
|
||||
libraries += [os.path.join(prefix, l)]
|
||||
break
|
||||
return libraries
|
||||
|
||||
def _filter_sysroot(self, s):
|
||||
if 'sysroot' in self.defines:
|
||||
sysroot = self.defines['sysroot']
|
||||
offset = 0
|
||||
while True:
|
||||
dash = s[offset:].find('-')
|
||||
if dash < 0:
|
||||
break
|
||||
if offset + dash + 2 < len(s) and s[offset + dash + 1] in 'LI':
|
||||
s = s[:offset + dash + 2] + sysroot + s[offset + dash + 2:]
|
||||
offset += dash + 1
|
||||
return s
|
||||
|
||||
def _filter_top_builddir(self, s):
|
||||
if 'top_builddir' in self.defines:
|
||||
top_builddir = self.defines['top_builddir']
|
||||
if self.file.startswith(top_builddir) and not s.startswith(top_builddir):
|
||||
offset = 0
|
||||
while True:
|
||||
dash = s[offset:].find('-')
|
||||
if dash < 0:
|
||||
break
|
||||
if offset + dash + 2 < len(s) and s[offset + dash + 1] in 'LI':
|
||||
s = s[:offset + dash + 2] + top_builddir + s[offset + dash + 2:]
|
||||
offset += dash + 1
|
||||
return s
|
||||
|
||||
def _filter_duplicates(self, s):
|
||||
clean = ''
|
||||
present = {}
|
||||
ss = shlex.split(s)
|
||||
i = 0
|
||||
while i < len(ss):
|
||||
added = False
|
||||
for do in package.dual_opts:
|
||||
if ss[i].startswith(do):
|
||||
if ss[i] == do:
|
||||
i += 1
|
||||
if i == len(ss):
|
||||
clean += ' %s' % (do)
|
||||
else:
|
||||
key = '%s%s' % (do, ss[i])
|
||||
if key not in present:
|
||||
if ' ' in ss[i]:
|
||||
clean += ' %s"%s"' % (do, ss[i])
|
||||
else:
|
||||
clean += ' %s' % (key)
|
||||
else:
|
||||
key = ss[i]
|
||||
if key not in present:
|
||||
clean += ' %s' % (key)
|
||||
added = True
|
||||
present[key] = True
|
||||
break
|
||||
if not added:
|
||||
if ss[i] not in present:
|
||||
clean += ' %s' % (ss[i])
|
||||
present[ss[i]] = True
|
||||
i += 1
|
||||
return clean
|
||||
|
||||
def _filter(self, s):
|
||||
s = self._filter_top_builddir(s)
|
||||
s = self._filter_sysroot(s)
|
||||
s = self._filter_duplicates(s)
|
||||
return s.strip()
|
||||
|
||||
def _splitter(self, pkg_list):
|
||||
pkgs = []
|
||||
pls = pkg_list.split()
|
||||
i = 0
|
||||
while i < len(pls):
|
||||
pkg = [pls[i]]
|
||||
i += 1
|
||||
if i < len(pls) and pls[i] in package.version_ops:
|
||||
pkg += [pls[i]]
|
||||
i += 1
|
||||
if i < len(ls):
|
||||
pkg += [pls[i]]
|
||||
i += 1
|
||||
pkgs += [pkg]
|
||||
return pkgs
|
||||
|
||||
def name_from_file(self, file = None):
|
||||
if file is None:
|
||||
file = self.file
|
||||
if file is None:
|
||||
return None
|
||||
name = os.path.basename(file)
|
||||
if name.endswith('.pc'):
|
||||
name = name[:-3]
|
||||
return name
|
||||
|
||||
def name(self):
|
||||
return self.name_
|
||||
|
||||
def file(self):
|
||||
return self.file_
|
||||
|
||||
def exists(self):
|
||||
ok = False
|
||||
if self.file_:
|
||||
ok = True
|
||||
if self.libraries:
|
||||
ok = True
|
||||
return ok
|
||||
|
||||
def load(self, name):
|
||||
if name in package.loaded:
|
||||
raise error('package already loaded: %s' % (name))
|
||||
if self.name_:
|
||||
self._clean()
|
||||
self.name_ = name
|
||||
file = self._find_package(name)
|
||||
self._log('load: %s (%s)' % (name, file))
|
||||
if file:
|
||||
if self.src:
|
||||
self.src.writelines('==%s%s' % ('=' * 80, os.linesep))
|
||||
self.src.writelines(' %s %s%s' % (file, '=' * (80 - len(file)), os.linesep))
|
||||
self.src.writelines('==%s%s' % ('=' * 80, os.linesep))
|
||||
f = open(file)
|
||||
tm = False
|
||||
for l in f.readlines():
|
||||
if self.src:
|
||||
self.src.writelines(l)
|
||||
l = l[:-1]
|
||||
hash = l.find('#')
|
||||
if hash >= 0:
|
||||
l = l[:hash]
|
||||
if len(l):
|
||||
d = 0
|
||||
define = False
|
||||
eq = l.find('=')
|
||||
dd = l.find(':')
|
||||
if eq > 0 and dd > 0:
|
||||
if eq < dd:
|
||||
define = True
|
||||
d = eq
|
||||
else:
|
||||
define = False
|
||||
d = dd
|
||||
elif eq >= 0:
|
||||
define = True
|
||||
d = eq
|
||||
elif dd >= 0:
|
||||
define = False
|
||||
d = dd
|
||||
if d > 0:
|
||||
lhs = l[:d].lower()
|
||||
rhs = l[d + 1:]
|
||||
if tm:
|
||||
print('define: ' + str(define) + ', lhs: ' + lhs + ', ' + rhs)
|
||||
if define:
|
||||
self.defines[lhs] = rhs
|
||||
else:
|
||||
self.fields[lhs] = rhs
|
||||
self.file_ = file
|
||||
else:
|
||||
self.libraries = self._find_libraries(name)
|
||||
for nt in package.node_types:
|
||||
requires = self.get(nt, private = False)
|
||||
if requires:
|
||||
for r in package.splitter(requires):
|
||||
if r[0] not in self.nodes[nt]:
|
||||
if r[0] in package.loaded:
|
||||
pkg = package.loaded[r[0]]
|
||||
else:
|
||||
pkg = package(r[0], self.prefix, self.output)
|
||||
ver = pkg.get('version')
|
||||
self._log(' checking: %s (%s) %s %s' % (r[0], ver, r[1], r[2]))
|
||||
if ver and package.check_versions(ver, r[1], r[2]):
|
||||
self.nodes[nt][r[0]] = pkg
|
||||
else:
|
||||
self._log('failed: %s (%s %s %s)' % (r[0], ver, r[1], r[2]))
|
||||
self.nodes['failed'][r[0]] = pkg
|
||||
if self.exists():
|
||||
package.loaded[name] = self
|
||||
|
||||
def get(self, label, private = True):
|
||||
if label.lower() not in self.fields:
|
||||
return None
|
||||
s = ''
|
||||
if self.file_:
|
||||
mre = re.compile('\$\{[^\}]+\}')
|
||||
s = self.fields[label.lower()]
|
||||
expanded = True
|
||||
tm = False
|
||||
while expanded:
|
||||
expanded = False
|
||||
if tm:
|
||||
self._log('pc:get: "' + s + '"')
|
||||
ms = mre.findall(s)
|
||||
for m in ms:
|
||||
mn = m[2:-1]
|
||||
if mn.lower() in self.defines:
|
||||
s = s.replace(m, self.defines[mn.lower()])
|
||||
expanded = True
|
||||
if label in package.get_recursion:
|
||||
for nt in package.node_types:
|
||||
if 'private' not in nt or ('private' in nt and private):
|
||||
for n in self.nodes[nt]:
|
||||
r = self.nodes[nt][n].get(label, private = private)
|
||||
self._log('node: %s: %s' % (self.nodes[nt][n].name(), r))
|
||||
if r:
|
||||
s += ' ' + r
|
||||
elif label == 'libs' and len(self.libraries):
|
||||
s = '-l%s' % (self.name_[3:])
|
||||
return self._filter(s)
|
||||
|
||||
def check(self, op, version):
|
||||
self._log('checking: %s %s %s' % (self.name_, op, version))
|
||||
ok = False
|
||||
if self.file_:
|
||||
pkgver = self.get('version')
|
||||
if pkgver is None:
|
||||
self._log('check: %s %s failed (no version)' % (op, version))
|
||||
return False
|
||||
ok = package.check_versions(pkgver, op, version)
|
||||
if ok:
|
||||
self._log('check: %s %s %s ok' % (pkgver, op, version))
|
||||
else:
|
||||
self._log('check: %s %s %s failed' % (pkgver, op, version))
|
||||
else:
|
||||
if len(self.libraries):
|
||||
ok = True
|
||||
else:
|
||||
self._log('check: %s not found' % (self.name_))
|
||||
return ok
|
Loading…
x
Reference in New Issue
Block a user