diff options
author | Mad Camel <madcamel@gmail.com> | 2024-02-18 16:49:33 +0000 |
---|---|---|
committer | Mad Camel <madcamel@gmail.com> | 2024-02-18 16:49:33 +0000 |
commit | 8edebcaf5ecdb7bcc731142921e16390436fc7a3 (patch) | |
tree | eba669dc30a217af0d8a958b324dcaf9772f6c54 /attoconf/lib | |
parent | 208f846464b96721d93436006365705fde3b6b2c (diff) | |
parent | f1a54c378b7b51b36103ad8111d66aef63e7f041 (diff) | |
download | attobuild-master.tar.gz attobuild-master.tar.bz2 attobuild-master.tar.xz attobuild-master.zip |
Import from specing's attoconf branch
See merge request legacy/attobuild!1
Diffstat (limited to 'attoconf/lib')
-rw-r--r-- | attoconf/lib/__init__.py | 0 | ||||
-rw-r--r-- | attoconf/lib/arches.py | 60 | ||||
-rw-r--r-- | attoconf/lib/c.py | 281 | ||||
-rw-r--r-- | attoconf/lib/config_hash.py | 48 | ||||
-rw-r--r-- | attoconf/lib/install.py | 271 | ||||
-rw-r--r-- | attoconf/lib/lex.py | 34 | ||||
-rw-r--r-- | attoconf/lib/make.py | 87 | ||||
-rw-r--r-- | attoconf/lib/pkg_config.py | 97 | ||||
-rw-r--r-- | attoconf/lib/templates.py | 77 | ||||
-rw-r--r-- | attoconf/lib/yacc.py | 34 |
10 files changed, 989 insertions, 0 deletions
diff --git a/attoconf/lib/__init__.py b/attoconf/lib/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/attoconf/lib/__init__.py diff --git a/attoconf/lib/arches.py b/attoconf/lib/arches.py new file mode 100644 index 0000000..67a84bd --- /dev/null +++ b/attoconf/lib/arches.py @@ -0,0 +1,60 @@ +# Copyright 2013-2014 Ben Longbons <b.r.longbons@gmail.com> +# +# 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 <http://www.gnu.org/licenses/>. + + + +from ..classy import ClassyProject +from ..types import triple, maybe + + +def build(build, BUILD): + pass + +def host(build, HOST): + if not HOST: + BUILD = build.vars['BUILD'] + build.vars['HOST'] = BUILD + +def target(build, TARGET): + if not TARGET: + HOST = build.vars['HOST'] + build.vars['TARGET'] = HOST + +class Arches2(ClassyProject): + __slots__ = () + + def arches(self): + super(Arches2, self).arches() + self.add_help('System types:', hidden=False) + self.add_option('--build', init='', + type=maybe(triple), check=build, + help='configure for building on BUILD', hidden=False, + help_def='native') + self.add_option('--host', init='', + type=maybe(triple), check=host, + help='cross-compile to build programs to run on HOST', + hidden=False, help_def='BUILD') + +# TODO figure out the mro implications when I use this +class Arches3(Arches2): + __slots__ = () + def arches(self): + super(Arches3, self).arches() + self.add_option('--target', init='', + type=maybe(triple), check=target, + help='configure for building compilers for TARGET', + hidden=False, help_def='HOST') diff --git a/attoconf/lib/c.py b/attoconf/lib/c.py new file mode 100644 index 0000000..98d88df --- /dev/null +++ b/attoconf/lib/c.py @@ -0,0 +1,281 @@ +# Copyright 2013-2014 Ben Longbons <b.r.longbons@gmail.com> +# +# 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 <http://www.gnu.org/licenses/>. + + + +import errno +import os +import subprocess + +from .arches import Arches2 +from ..types import ShellList + +class TestError(Exception): + pass + +def do_exec(build, args): + p = subprocess.Popen(args.list, cwd=build.builddir, + stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + out, _ = p.communicate() + retcode = p.wait() + return retcode, out + + +class TempFile: + ''' context manager that optionally creates and then removes a file + ''' + __slots__ = ('filename') + + def __init__(self, filename, content): + self.filename = filename + if content is not None: + with open(filename, 'x') as of: + of.write(content) + else: + # TODO: raise OSError(errno.EEXIST) if file already exists + pass + + def __enter__(self): + pass + + def __exit__(self, type, value, traceback): + try: + os.remove(self.filename) + except OSError as e: + if e.errno != errno.ENOENT: + raise + + +def try_compile_c(build, body, CFLAGS=[], CPPFLAGS=[]): + CC = build.vars['CC'] + CFLAGS = build.vars['CFLAGS'] + CFLAGS + CPPFLAGS = build.vars['CPPFLAGS'] + CPPFLAGS + in_ = 'atto-test.c' + ins = [in_] + out = 'atto-test.o' + + args = CC + CFLAGS + CPPFLAGS + ['-c', '-o', out, in_] + with TempFile(in_, body), TempFile(out, None): + status, error = do_exec(build, args) + if status: + raise TestError(error) + +def try_compile_link_c(build, body, CFLAGS=[], CPPFLAGS=[], LDFLAGS=[], LIBS=[]): + CC = build.vars['CC'] + CFLAGS = build.vars['CFLAGS'] + CFLAGS + CPPFLAGS = build.vars['CPPFLAGS'] + CPPFLAGS + LDFLAGS = build.vars['LDFLAGS'] + LDFLAGS + LIBS = build.vars['LIBS'] + LIBS + in_ = 'atto-test.c' + ins = [in_] + out = 'atto-test' + + args = CC + CFLAGS + CPPFLAGS + LDFLAGS + ins + LIBS + ['-o', out] + with TempFile(in_, body), TempFile(out, None): + status, error = do_exec(build, args) + if status: + raise TestError(error) + +def try_compile_cxx(build, body, CXXFLAGS=[], CPPFLAGS=[]): + CXX = build.vars['CXX'] + CXXFLAGS = build.vars['CXXFLAGS'] + CXXFLAGS + CPPFLAGS = build.vars['CPPFLAGS'] + CPPFLAGS + in_ = 'atto-test.cxx' + out = 'atto-test.o' + + args = CXX + CXXFLAGS + CPPFLAGS + ['-c', '-o', out, in_] + with TempFile(in_, body), TempFile(out, None): + status, error = do_exec(build, args) + if status: + raise TestError(error) + +def try_compile_link_cxx(build, body, CXXFLAGS=[], CPPFLAGS=[], LDFLAGS=[], LIBS=[]): + CXX = build.vars['CXX'] + CXXFLAGS = build.vars['CXXFLAGS'] + CXXFLAGS + CPPFLAGS = build.vars['CPPFLAGS'] + CPPFLAGS + LDFLAGS = build.vars['LDFLAGS'] + LDFLAGS + LIBS = build.vars['LIBS'] + LIBS + in_ = 'atto-test.cxx' + ins = [in_] + out = 'atto-test' + + args = CXX + CXXFLAGS + CPPFLAGS + LDFLAGS + ins + LIBS + ['-o', out] + with TempFile(in_, body), TempFile(out, None): + status, error = do_exec(build, args) + if status: + raise TestError(error) + +if 0: + def try_linkonly_c(build, ins, LDFLAGS=[], LIBS=[]): + CC = build.vars['CC'] + LDFLAGS = build.vars['LDFLAGS'] + LDFLAGS + LIBS = build.vars['LIBS'] + LIBS + out = 'atto-test' + + args = CC + LDFLAGS + ins + LIBS + ['-o', out] + with TempFile(out, None): + status, error = do_exec(build, args) + if status: + raise TestError(error) + +def try_compile_link2_c(build, body, CFLAGS=[], CPPFLAGS=[], LDFLAGS=[], LIBS=[]): + CC = build.vars['CC'] + CFLAGS = build.vars['CFLAGS'] + CFLAGS + CPPFLAGS = build.vars['CPPFLAGS'] + CPPFLAGS + LDFLAGS = build.vars['LDFLAGS'] + LDFLAGS + LIBS = build.vars['LIBS'] + LIBS + in_ = 'atto-test.c' + ins = [in_] + mid = 'atto-test.o' + mids = [mid] + out = 'atto-test' + + args1 = CC + CFLAGS + CPPFLAGS + ['-c', '-o', mid, in_] + args2 = CC + LDFLAGS + mids + LIBS + ['-o', out] + with TempFile(mid, None): + with TempFile(in_, body): + status, error = do_exec(build, args1) + if status: + raise TestError(error) + + with TempFile(out, None): + status, error = do_exec(build, args2) + if status: + raise TestError(error) + +if 0: + def try_linkonly_cxx(build, ins, LDFLAGS=[], LIBS=[]): + CXX = build.vars['CXX'] + LDFLAGS = build.vars['LDFLAGS'] + LDFLAGS + LIBS = build.vars['LIBS'] + LIBS + out = 'atto-test' + + args = CXX + LDFLAGS + ins + LIBS + ['-o', out] + with TempFile(out, None): + status, error = do_exec(build, args) + if status: + raise TestError(error) + +def try_compile_link2_cxx(build, body, CXXFLAGS=[], CPPFLAGS=[], LDFLAGS=[], LIBS=[]): + CXX = build.vars['CXX'] + CXXFLAGS = build.vars['CXXFLAGS'] + CXXFLAGS + CPPFLAGS = build.vars['CPPFLAGS'] + CPPFLAGS + LDFLAGS = build.vars['LDFLAGS'] + LDFLAGS + LIBS = build.vars['LIBS'] + LIBS + in_ = 'atto-test.cxx' + ins = [in_] + mid = 'atto-test.o' + mids = [mid] + out = 'atto-test' + + args1 = CXX + CXXFLAGS + CPPFLAGS + ['-c', '-o', mid, in_] + args2 = CXX + LDFLAGS + mids + LIBS + ['-o', out] + with TempFile(mid, None): + with TempFile(in_, body): + status, error = do_exec(build, args1) + if status: + raise TestError(error) + + with TempFile(out, None): + status, error = do_exec(build, args2) + if status: + raise TestError(error) + + +def ldflags(build, LDFLAGS): + pass + +def libs(build, LIBS): + # compatibility + build.vars['LDLIBS'] = LIBS + +def cppflags(build, CPPFLAGS): + pass + +def cc(build, CC): + if CC.list == []: + HOST = build.vars['HOST'] + if HOST: + build.vars['CC'].list = [HOST + '-gcc'] + else: + build.vars['CC'].list = ['gcc'] + +def cflags(build, CFLAGS): + try_compile_c(build, 'int main() {}\n') + try_compile_link_c(build, 'int main() {}\n') + try_compile_link2_c(build, 'int main() {}\n') + +def cxx(build, CXX): + if CXX.list == []: + HOST = build.vars['HOST'] + if HOST: + build.vars['CXX'].list = [HOST + '-g++'] + else: + build.vars['CXX'].list = ['g++'] + +def cxxflags(build, CXXFLAGS): + try_compile_cxx(build, 'int main() {}\n') + try_compile_link_cxx(build, 'int main() {}\n') + try_compile_link2_cxx(build, 'int main() {}\n') + +class Link(Arches2): + __slots__ = () + def vars(self): + super(Link, self).vars() + self.add_option('LDFLAGS', init=[], + type=ShellList, check=ldflags, + help='linker flags, e.g. -L<lib dir> if you have libraries in a nonstandard directory <lib dir>', + hidden=False) + self.add_option('LIBS', init=[], + type=ShellList, check=libs, + help='libraries to pass to the linker, e.g. -l<library>', + hidden=False) + self.order.append('LDLIBS') #TODO remove for 1.0 + +class Preprocess(Arches2): + __slots__ = () + def vars(self): + super(Preprocess, self).vars() + self.add_option('CPPFLAGS', init=[], + type=ShellList, check=cppflags, + help='C/C++/Objective C preprocessor flags, e.g. -I<include dir> if you have headers in a nonstandard directory <include dir>', + hidden=False) + +class C(Link, Preprocess): + __slots__ = () + def vars(self): + super(C, self).vars() + self.add_option('CC', init=[], + type=ShellList, check=cc, + help='C compiler command', hidden=False, + help_def='HOST-gcc') + self.add_option('CFLAGS', init=['-O2', '-g'], + type=ShellList, check=cflags, + help='C compiler flags', hidden=False) + +class Cxx(Link, Preprocess): + __slots__ = () + def vars(self): + super(Cxx, self).vars() + self.add_option('CXX', init=[], + type=ShellList, check=cxx, + help='C++ compiler command', hidden=False, + help_def='HOST-g++') + self.add_option('CXXFLAGS', init=['-O2', '-g'], + type=ShellList, check=cxxflags, + help='C++ compiler flags', hidden=False) diff --git a/attoconf/lib/config_hash.py b/attoconf/lib/config_hash.py new file mode 100644 index 0000000..b3cb423 --- /dev/null +++ b/attoconf/lib/config_hash.py @@ -0,0 +1,48 @@ +# Copyright 2013 Ben Longbons <b.r.longbons@gmail.com> +# +# 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 <http://www.gnu.org/licenses/>. + + + +from hashlib import md5 +import os + +from ..classy import ClassyProject + + +def calc_hash(build): + hash = md5() + for var, val in sorted(build.vars.items()): + hash.update(('%s = %s\n' % (var, val)).encode(encoding = 'UTF-8', errors = 'strict')) + return hash.hexdigest() + + +def add_config_hash(build): + print('Generating a hash of config options ...') + build.vars['CONFIG_HASH'] = calc_hash(build) + + +class ConfigHash(ClassyProject): + ''' Post hook add the build hash. + + This should be run before any other post hooks. + ''' + __slots__ = () + + def post(self): + self.order.insert(0, 'CONFIG_HASH') + self.checks.append(add_config_hash) + super(ConfigHash, self).post() diff --git a/attoconf/lib/install.py b/attoconf/lib/install.py new file mode 100644 index 0000000..04b5c5a --- /dev/null +++ b/attoconf/lib/install.py @@ -0,0 +1,271 @@ +# Copyright 2013-2014 Ben Longbons <b.r.longbons@gmail.com> +# +# 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 <http://www.gnu.org/licenses/>. + + + +import os + +from ..classy import ClassyProject +from ..types import shell_word, filepath, quoted_string, maybe + + +def package(build, PACKAGE): + pass + +def package_name(build, NAME): + pass + +def prefix(build, PREFIX): + pass + +def exec_prefix(build, EPREFIX): + if not EPREFIX: + PREFIX = build.vars['PREFIX'] + build.vars['EXEC_PREFIX'] = PREFIX + build.vars['EPREFIX'] = build.vars['EXEC_PREFIX'] + +def bindir(build, DIR): + if not DIR: + EPREFIX = build.vars['EPREFIX'] + build.vars['BINDIR'] = os.path.join(EPREFIX, 'bin') + +def sbindir(build, DIR): + if not DIR: + EPREFIX = build.vars['EPREFIX'] + build.vars['SBINDIR'] = os.path.join(EPREFIX, 'sbin') + +def libexecdir(build, DIR): + if not DIR: + EPREFIX = build.vars['EPREFIX'] + build.vars['LIBEXECDIR'] = os.path.join(EPREFIX, 'libexec') + +def sysconfdir(build, DIR): + if not DIR: + PREFIX = build.vars['PREFIX'] + build.vars['SYSCONFDIR'] = os.path.join(PREFIX, 'etc') + +def sharedstatedir(build, DIR): + if not DIR: + PREFIX = build.vars['PREFIX'] + build.vars['SHAREDSTATEDIR'] = os.path.join(PREFIX, 'com') + +def localstatedir(build, DIR): + if not DIR: + PREFIX = build.vars['PREFIX'] + build.vars['LOCALSTATEDIR'] = os.path.join(PREFIX, 'var') + +def libdir(build, DIR): + if not DIR: + EPREFIX = build.vars['EPREFIX'] + build.vars['LIBDIR'] = os.path.join(EPREFIX, 'lib') + +def includedir(build, DIR): + if not DIR: + PREFIX = build.vars['PREFIX'] + build.vars['INCLUDEDIR'] = os.path.join(PREFIX, 'include') + +def oldincludedir(build, DIR): + pass + +def datarootdir(build, DIR): + if not DIR: + PREFIX = build.vars['PREFIX'] + build.vars['DATAROOTDIR'] = os.path.join(PREFIX, 'share') + +def datadir(build, DIR): + if not DIR: + DATAROOTDIR = build.vars['DATAROOTDIR'] + build.vars['DATADIR'] = DATAROOTDIR + +def packagedatadir(build, DIR): + if not DIR: + DATADIR = build.vars['DATADIR'] + PACKAGE = build.vars['PACKAGE'] + build.vars['PACKAGEDATADIR'] = os.path.join(DATADIR, PACKAGE) + +def infodir(build, DIR): + if not DIR: + DATAROOTDIR = build.vars['DATAROOTDIR'] + build.vars['INFODIR'] = os.path.join(DATAROOTDIR, 'info') + +def localedir(build, DIR): + if not DIR: + DATAROOTDIR = build.vars['DATAROOTDIR'] + build.vars['LOCALEDIR'] = os.path.join(DATAROOTDIR, 'locale') + +def mandir(build, DIR): + if not DIR: + DATAROOTDIR = build.vars['DATAROOTDIR'] + build.vars['MANDIR'] = os.path.join(DATAROOTDIR, 'man') + +def docdir(build, DIR): + if not DIR: + DATAROOTDIR = build.vars['DATAROOTDIR'] + PACKAGE = build.vars['PACKAGE'] + build.vars['DOCDIR'] = os.path.join(DATAROOTDIR, 'doc', PACKAGE) + +def htmldir(build, DIR): + if not DIR: + DOCDIR = build.vars['DOCDIR'] + build.vars['HTMLDIR'] = DOCDIR + +def dvidir(build, DIR): + if not DIR: + DOCDIR = build.vars['DOCDIR'] + build.vars['DVIDIR'] = DOCDIR + +def pdfdir(build, DIR): + if not DIR: + DOCDIR = build.vars['DOCDIR'] + build.vars['PDFDIR'] = DOCDIR + +def psdir(build, DIR): + if not DIR: + DOCDIR = build.vars['DOCDIR'] + build.vars['PSDIR'] = DOCDIR + + +class Install(ClassyProject): + __slots__ = () + _merge_slots_ = ('package', 'package_name') + + # Compatibility with configure written for attoconf < 0.7 + # In attoconf 1.0, the positional srcdir argument will go away, + # the None default and the .set_package function will be removed. + # (Note: when bisecting, always force checkout attoconf!) + def __init__(self, srcdir, package=None, package_name=None, **kwargs): + super(Install, self).__init__(srcdir=srcdir, **kwargs) + self.set_package(package, package_name) + + def set_package(self, package, package_name): + if package is not None: + assert self.package is None + self.package = package + self.package_name = package_name + if package is not None: + self._do_jiggle() + + def general(self): + super(Install, self).general() + self.add_option('--package', init=self.package, + type=shell_word, check=package, + help='Short name of this package (don\'t change!)', + hidden=True) + self.add_option('--package-name', init=self.package_name, + type=quoted_string, check=package_name, + help='Long name of this package (don\'t change)', + hidden=True, + help_var='NAME') + + def paths(self): + super(Install, self).paths() + + self.add_help('Installation directories:', hidden=False) + self.add_option('--prefix', init='/usr/local', + type=filepath, check=prefix, + help='install architecture-independent files in PREFIX', + hidden=False) + self.add_option('--exec-prefix', init='', + type=maybe(filepath), check=exec_prefix, + help='install architecture-dependent files in EPREFIX', + hidden=False, + help_var='EPREFIX', help_def='PREFIX') + self.order.append('EPREFIX') # TODO remove for 1.0 + self.order.append(None) + + self.add_help('Fine tuning of the installation directories:', + hidden=False) + self.add_option('--bindir', init='', + type=maybe(filepath), check=bindir, + help='user executables', hidden=False, + help_var='DIR', help_def='EPREFIX/bin') + self.add_option('--sbindir', init='', + type=maybe(filepath), check=sbindir, + help='system admin executables', hidden=False, + help_var='DIR', help_def='EPREFIX/sbin') + self.add_option('--libexecdir', init='', + type=maybe(filepath), check=libexecdir, + help='program executables', hidden=False, + help_var='DIR', help_def='EPREFIX/libexec') + self.add_option('--sysconfdir', init='', + type=maybe(filepath), check=sysconfdir, + help='read-only single-machine data', hidden=False, + help_var='DIR', help_def='PREFIX/etc') + self.add_option('--sharedstatedir', init='', + type=maybe(filepath), check=sharedstatedir, + help='modifiable architecture-independent data', hidden=False, + help_var='DIR', help_def='PREFIX/com') + self.add_option('--localstatedir', init='', + type=maybe(filepath), check=localstatedir, + help='modifiable single-machine data', hidden=False, + help_var='DIR', help_def='PREFIX/var') + self.add_option('--libdir', init='', + type=maybe(filepath), check=libdir, + help='object code libraries', hidden=False, + help_var='DIR', help_def='EPREFIX/lib') + self.add_option('--includedir', init='', + type=maybe(filepath), check=includedir, + help='C header files', hidden=False, + help_var='DIR', help_def='PREFIX/include') + self.add_option('--oldincludedir', init='/usr/include', + type=filepath, check=oldincludedir, + help='C header files for non-gcc', hidden=False, + help_var='DIR') + self.add_option('--datarootdir', init='', + type=maybe(filepath), check=datarootdir, + help='read-only arch.-independent data root', hidden=False, + help_var='DIR', help_def='PREFIX/share') + self.add_option('--datadir', init='', + type=maybe(filepath), check=datadir, + help='read-only architecture-independent data', hidden=False, + help_var='DIR', help_def='DATAROOTDIR') + self.add_option('--packagedatadir', init='', + type=maybe(filepath), check=packagedatadir, + help='data specific to this package (please set datadir instead)', hidden=False, + help_var='DIR', help_def='DATADIR/PACKAGE') + self.add_option('--infodir', init='', + type=maybe(filepath), check=infodir, + help='info documentation', hidden=False, + help_var='DIR', help_def='DATAROOTDIR/info') + self.add_option('--localedir', init='', + type=maybe(filepath), check=localedir, + help='locale-dependent data', hidden=False, + help_var='DIR', help_def='DATAROOTDIR/locale') + self.add_option('--mandir', init='', + type=maybe(filepath), check=mandir, + help='man documentation', hidden=False, + help_var='DIR', help_def='DATAROOTDIR/man') + self.add_option('--docdir', init='', + type=maybe(filepath), check=docdir, + help='documentation root', hidden=False, + help_var='DIR', help_def='DATAROOTDIR/doc/PACKAGE') + self.add_option('--htmldir', init='', + type=maybe(filepath), check=htmldir, + help='html documentation', hidden=False, + help_var='DIR', help_def='DOCDIR') + self.add_option('--dvidir', init='', + type=maybe(filepath), check=dvidir, + help='dvi documentation', hidden=False, + help_var='DIR', help_def='DOCDIR') + self.add_option('--pdfdir', init='', + type=maybe(filepath), check=pdfdir, + help='pdf documentation', hidden=False, + help_var='DIR', help_def='DOCDIR') + self.add_option('--psdir', init='', + type=maybe(filepath), check=psdir, + help='ps documentation', hidden=False, + help_var='DIR', help_def='DOCDIR') diff --git a/attoconf/lib/lex.py b/attoconf/lib/lex.py new file mode 100644 index 0000000..27c2a7e --- /dev/null +++ b/attoconf/lib/lex.py @@ -0,0 +1,34 @@ +# Copyright 2013 Ben Longbons <b.r.longbons@gmail.com> +# +# 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 <http://www.gnu.org/licenses/>. + + + +from ..classy import ClassyProject +from ..types import ShellList + +def flex(build, FLEX): + # TODO actually test it + pass + +class Flex(ClassyProject): + __slots__ = () + def vars(self): + super(Flex, self).vars() + self.add_option('FLEX', init=['flex'], + type=ShellList, check=flex, + help='Lexical analyzer command', + hidden=False) diff --git a/attoconf/lib/make.py b/attoconf/lib/make.py new file mode 100644 index 0000000..ca70f34 --- /dev/null +++ b/attoconf/lib/make.py @@ -0,0 +1,87 @@ +# Copyright 2013-2014 Ben Longbons <b.r.longbons@gmail.com> +# +# 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 <http://www.gnu.org/licenses/>. + + + +import os +import sys + +from ..classy import ClassyProject +from ..version import full_version + +blacklist = frozenset(''.join(chr(i) for i in range(0x20)) + '#$') +def validate(s): + return s == s.strip() and not frozenset(s) & blacklist + + +class MakeHook(object): + __slots__ = ('infile', 'outfile') + def __init__(self, infile, outfile): + self.infile = infile + self.outfile = outfile + + def __call__(self, build): + if self.outfile is None: + # if there are multiple backends + print('Skipping generation of a makefile') + return + with open(os.path.join(build.builddir, self.outfile), 'w') as out: + print('Generating a makefile ...') + out.write('# This part was generated by %s\n' % full_version) + build.vars['SRC_DIR'] = build.relative_source() + for var in build.project.order: + if var is None: + out.write('\n') + continue + val = build.vars[var] + out.write('%s = %s\n' % (var, val)) + if self.infile is not None: + out.write('# The rest was copied from %s\n' % self.infile) + infile = os.path.join(build.project.srcdir, self.infile) + with open(infile) as in_: + for line in in_: + assert line.endswith('\n') + out.write(line) + + +class Make(ClassyProject): + ''' Post hook to generate a Makefile from Makefile.in + ''' + __slots__ = () + _merge_slots_ = ('make_in', 'make_out') + + # compatibility with attoconf < 0.7 + def __init__(self, srcdir, + make_infile='Makefile.in', + make_outfile='Makefile', + **kwargs): + super(Make, self).__init__(srcdir=srcdir, **kwargs) + self.set_make_infile(make_infile) + self.set_make_outfile(make_outfile) # relative to build dir + + def set_make_infile(self, ipath): + self.make_in = ipath + + def set_make_outfile(self, opath): + self.make_out = opath + + def post(self): + super(Make, self).post() + if 'SRC_DIR' in self.order: + sys.exit('ERROR: Incompatible generator hooks!') + self.order.insert(0, 'SRC_DIR') + self.checks.append(MakeHook(self.make_in, self.make_out)) diff --git a/attoconf/lib/pkg_config.py b/attoconf/lib/pkg_config.py new file mode 100644 index 0000000..5bc846b --- /dev/null +++ b/attoconf/lib/pkg_config.py @@ -0,0 +1,97 @@ +# Copyright 2015 Ben Longbons <b.r.longbons@gmail.com> +# +# 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 <http://www.gnu.org/licenses/>. + + + +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) diff --git a/attoconf/lib/templates.py b/attoconf/lib/templates.py new file mode 100644 index 0000000..7c26133 --- /dev/null +++ b/attoconf/lib/templates.py @@ -0,0 +1,77 @@ +# Copyright 2013-2014 Ben Longbons <b.r.longbons@gmail.com> +# +# 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 <http://www.gnu.org/licenses/>. + + + +import os +import sys + +from ..classy import ClassyProject + + +class TemplateHook(object): + __slots__ = ('outfiles') + def __init__(self, outfiles): + self.outfiles = outfiles + + def __call__(self, build): + build.vars['SRC_DIR'] = build.relative_source() + unseen = set(build.project.order) + if None in unseen: + unseen.remove(None) + for outfile in self.outfiles: + infile = outfile + '.in' + print('Generating %s from %s' % (outfile, infile)) + # by replacing all instances of @VARIABLE@ with the value + + slurpee = open(os.path.join(build.project.srcdir, infile)).read() + for var in build.project.order: + if var is None: + continue + val = build.vars[var] + key = '@' + var + '@' + if key not in slurpee: + continue + if var in unseen: + unseen.remove(var) + slurpee = slurpee.replace(key, str(val)) + with open(os.path.join(build.builddir, outfile), 'w') as out: + out.write(slurpee) + if unseen: + print('WARNING: variables not used:') + print(' ' + '\n '.join(unseen)) + # lone @s may legitimately appear in the makefile. + # paired @s, which would be a forgotten subst, will be obvious. + + +class Templates(ClassyProject): + ''' Post hook to generate output files from *.in templates + ''' + __slots__ = () + _merge_slots_ = ('template_files') + + # this class didn't exist in attoconf < 0.7, no need for compatibility + def __init__(self, template_files, **kwargs): + super(Templates, self).__init__(**kwargs) + self.template_files = template_files + + def post(self): + super(Templates, self).post() + if 'SRC_DIR' in self.order: + sys.exit('ERROR: Incompatible generator hooks!') + self.order.insert(0, 'SRC_DIR') + self.checks.append(TemplateHook(self.template_files)) diff --git a/attoconf/lib/yacc.py b/attoconf/lib/yacc.py new file mode 100644 index 0000000..c3fd1de --- /dev/null +++ b/attoconf/lib/yacc.py @@ -0,0 +1,34 @@ +# Copyright 2013 Ben Longbons <b.r.longbons@gmail.com> +# +# 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 <http://www.gnu.org/licenses/>. + + + +from ..classy import ClassyProject +from ..types import ShellList + +def bison(build, BISON): + # TODO actually test it + pass + +class Bison(ClassyProject): + __slots__ = () + def vars(self): + super(Bison, self).vars() + self.add_option('BISON', init=['bison'], + type=ShellList, check=bison, + help='Lexical analyzer command', + hidden=False) |