From 621af313a8dfe46df159766fbe158af5dd8cb6ad Mon Sep 17 00:00:00 2001 From: Ben Longbons Date: Wed, 30 Sep 2015 23:50:21 -0700 Subject: Add support for finding dependencies via pkg-config --- attoconf/_version.py | 4 +- attoconf/classy.py | 12 +++++- attoconf/lib/pkg_config.py | 97 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 attoconf/lib/pkg_config.py diff --git a/attoconf/_version.py b/attoconf/_version.py index 791d3d9..d4930e4 100644 --- a/attoconf/_version.py +++ b/attoconf/_version.py @@ -7,11 +7,11 @@ major = 0 # Incremented for releases with compatible API additions. # This is the number that is usually incremented. -minor = 9 +minor = 10 # Incremented if there is a bugfix release. # Might not be contiguous. -patch = 1 +patch = 0 # Reserved for distributors and forks. # Contains arbitrary text, but no parentheses or newlines. diff --git a/attoconf/classy.py b/attoconf/classy.py index 98d54aa..ba06fcc 100644 --- a/attoconf/classy.py +++ b/attoconf/classy.py @@ -1,4 +1,4 @@ -# Copyright 2013-2014 Ben Longbons +# Copyright 2013-2015 Ben Longbons # # This file is part of attoconf. # @@ -35,6 +35,12 @@ class PolymorphicSlotMergerMetaclass(type): assert dct['__slots__'] == () return type.__new__(meta, name, bases, dct) + # TODO: remove *args for 1.0 + def __call__(cls, *args, **kwargs): + instance = type.__call__(cls, *args, **kwargs) + instance._do_jiggle() + return instance + # TODO: remove this for 1.0 def add_slots(cls): @@ -50,7 +56,11 @@ class ClassyProject(Project): def __init__(self, srcdir): super(ClassyProject, self).__init__(srcdir) + # TODO: remove this for 1.0 def jiggle(self): + pass + + def _do_jiggle(self): self.order.append(None) self.general() self.order.append(None) diff --git a/attoconf/lib/pkg_config.py b/attoconf/lib/pkg_config.py new file mode 100644 index 0000000..00bfba8 --- /dev/null +++ b/attoconf/lib/pkg_config.py @@ -0,0 +1,97 @@ +# Copyright 2015 Ben Longbons +# +# This file is part of attoconf. +# +# attoconf is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# attoconf is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with attoconf. If not, see . + +from __future__ import print_function, division, absolute_import + +import os +import sys + +from ..classy import ClassyProject +from ..types import enum, ShellList + +from .c import do_exec, TestError, C, Cxx + + +yesno = enum('yes', 'no') + +def run_pkg_config(build, *args): + PKG_CONFIG = build.vars['PKG_CONFIG'] + status, output = do_exec(build, PKG_CONFIG + list(args)) + if status: + raise TestError(output) + return output.strip() + +def check_pkg_config(build, PKG_CONFIG): + version = run_pkg_config(build, '--version') + print('Found pkg-config: %s' % version) + +def package_check(build, package, **var): + assert len(var) == 1 + _package, enabled = var.popitem() + enabled = enabled == 'yes' + modversion = run_pkg_config(build, '--modversion', package) + print("Found dependency '%s': %s" % (package, modversion)) + cppflags = run_pkg_config(build, '--cflags-only-I', package) + cflags = run_pkg_config(build, '--cflags-only-other', package) + ldflags = run_pkg_config(build, '--libs-only-L', '--libs-only-other', package) + libs = run_pkg_config(build, '--libs-only-l', package) + + build.vars['CPPFLAGS'] += cppflags + if 'CFLAGS' in build.vars: + build.vars['CFLAGS'] += cflags + if 'CXXFLAGS' in build.vars: + build.vars['CXXFLAGS'] += cflags + build.vars['LDFLAGS'] += ldflags + build.vars['LIBS'] += libs + +class PkgConfig(ClassyProject): + ''' Fill CFLAGS etc by pkg-config for dependencies. + ''' + __slots__ = () + _merge_slots_ = ('required_packages', 'optional_packages') + + def __init__(self, required_packages, optional_packages, **kwargs): + assert isinstance(self, (C, Cxx)) + super(PkgConfig, self).__init__(**kwargs) + self.required_packages = required_packages + self.optional_packages = optional_packages + + def vars(self): + super(PkgConfig, self).vars() + self.add_option('PKG_CONFIG', init=['pkg-config'], + type=ShellList, check=check_pkg_config, + help='Tool to find dependencies', hidden=False) + + def packages(self): + super(PkgConfig, self).packages() + for package in self.required_packages: + self._pkg_config_add_package(package, True) + for package in self.optional_packages: + self._pkg_config_add_package(package, False) + + def _pkg_config_add_package(self, package, hidden): + positive = '--with-' + package + negative = '--without-' + package + #check = package_required_check if hidden else package_optional_check + check = lambda build, **kwargs: package_check(build, package, **kwargs) + level = 'required' if hidden else 'optional' + help = "Build with %s dependency '%s'" % (level, package) + self.add_option(positive, type=yesno, hidden=hidden, init='yes', check=check, help=help) + # TODO: instead reveal one of the aliases and hide the main + # this requires messing with help slightly + self.add_alias(positive, [positive + '=yes'], help=None, hidden=True) + self.add_alias(negative, [positive + '=no'], help=None, hidden=True) -- cgit v1.2.3-70-g09d2