diff --git a/source-builder/bin/pkg-config b/source-builder/bin/pkg-config deleted file mode 100755 index 1ae1284..0000000 --- a/source-builder/bin/pkg-config +++ /dev/null @@ -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) diff --git a/source-builder/defaults.mc b/source-builder/defaults.mc index 1030825..7b8d3b6 100644 --- a/source-builder/defaults.mc +++ b/source-builder/defaults.mc @@ -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 diff --git a/source-builder/pkg-config b/source-builder/pkg-config new file mode 100755 index 0000000..6d603c7 --- /dev/null +++ b/source-builder/pkg-config @@ -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) diff --git a/source-builder/sb/config.py b/source-builder/sb/config.py index ff63b32..4da00f0 100644 --- a/source-builder/sb/config.py +++ b/source-builder/sb/config.py @@ -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 diff --git a/source-builder/sb/pkgconfig.py b/source-builder/sb/pkgconfig.py new file mode 100755 index 0000000..22c27b7 --- /dev/null +++ b/source-builder/sb/pkgconfig.py @@ -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