summaryrefslogtreecommitdiff
path: root/attoconf/lib
diff options
context:
space:
mode:
authorBen Longbons <b.r.longbons@gmail.com>2013-08-05 15:41:04 -0700
committerBen Longbons <b.r.longbons@gmail.com>2013-08-05 15:41:04 -0700
commitd7bb91cd264300351e94e37a84b1de45f2312745 (patch)
tree9464fd72647146c2642ab6b493f858afcfe51566 /attoconf/lib
parent3a475005ce9f48b6bbf45a18660dbed6c4921269 (diff)
downloadattobuild-d7bb91cd264300351e94e37a84b1de45f2312745.tar.gz
attobuild-d7bb91cd264300351e94e37a84b1de45f2312745.tar.bz2
attobuild-d7bb91cd264300351e94e37a84b1de45f2312745.tar.xz
attobuild-d7bb91cd264300351e94e37a84b1de45f2312745.zip
Add a classy API, and use it to implement all the common options
Diffstat (limited to 'attoconf/lib')
-rw-r--r--attoconf/lib/__init__.py0
-rw-r--r--attoconf/lib/arches.py68
-rw-r--r--attoconf/lib/c.py268
-rw-r--r--attoconf/lib/install.py320
-rw-r--r--attoconf/lib/make.py94
5 files changed, 750 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..bafef38
--- /dev/null
+++ b/attoconf/lib/arches.py
@@ -0,0 +1,68 @@
+# 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 __future__ import print_function, division, absolute_import
+
+from ..classy import ClassyProject
+
+def triple(s):
+ # Triples do not, in fact, follow a regular pattern.
+ # Some have only two segments, some appear to have four ...
+ # Also, sometimes a wrong thing is used as a triple.
+ # All we *really* care about is generating the tool names.
+ if s.startswith('-') or s.endswith('-') or '-' not in s[1:-1]:
+ raise ValueError('Probably not a triple')
+ return s
+
+def host(build, HOST):
+ if HOST is None:
+ BUILD, origin = build.vars['BUILD']
+ if origin != 'default':
+ origin = 'derived from BUILD'
+ build.vars['HOST'] = (BUILD, origin)
+
+def target(build, TARGET):
+ if TARGET is None:
+ HOST, origin = build.vars['HOST']
+ if origin != 'default':
+ origin = 'derived from HOST'
+ build.vars['TARGET'] = (HOST, origin)
+
+class Arches2(ClassyProject):
+ __slots__ = ()
+
+ def arches(self):
+ super(Arches2, self).arches()
+ self.add_help('System types:', hidden=False)
+ self.add_option('--build', init=None,
+ type=triple, check=None,
+ help='configure for building on BUILD', hidden=False,
+ help_def='native')
+ self.add_option('--host', init=None,
+ type=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=None,
+ type=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..4d3f252
--- /dev/null
+++ b/attoconf/lib/c.py
@@ -0,0 +1,268 @@
+# 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 __future__ import print_function, division, absolute_import
+
+import errno
+import os
+from shlex import split as shell
+import subprocess
+
+from .arches import Arches2
+
+class TestError(Exception):
+ pass
+
+def do_exec(build, args):
+ p = subprocess.Popen(args, 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, 'wx') 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'][0]
+ CFLAGS = build.vars['CFLAGS'][0] + CFLAGS
+ CPPFLAGS = build.vars['CPPFLAGS'][0] + 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=[], LDLIBS=[]):
+ CC = build.vars['CC'][0]
+ CFLAGS = build.vars['CFLAGS'][0] + CFLAGS
+ CPPFLAGS = build.vars['CPPFLAGS'][0] + CPPFLAGS
+ LDFLAGS = build.vars['LDFLAGS'][0] + LDFLAGS
+ LDLIBS = build.vars['LDLIBS'][0] + LDLIBS
+ in_ = 'atto-test.c'
+ ins = [in_]
+ out = 'atto-test'
+
+ args = CC + CFLAGS + CPPFLAGS + LDFLAGS + ins + LDLIBS + ['-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'][0]
+ CXXFLAGS = build.vars['CXXFLAGS'][0] + CXXFLAGS
+ CPPFLAGS = build.vars['CPPFLAGS'][0] + 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=[], LDLIBS=[]):
+ CXX = build.vars['CXX'][0]
+ CXXFLAGS = build.vars['CXXFLAGS'][0] + CXXFLAGS
+ CPPFLAGS = build.vars['CPPFLAGS'][0] + CPPFLAGS
+ LDFLAGS = build.vars['LDFLAGS'][0] + LDFLAGS
+ LDLIBS = build.vars['LDLIBS'][0] + LDLIBS
+ in_ = 'atto-test.cxx'
+ out = 'atto-test'
+
+ args = CXX + CXXFLAGS + CPPFLAGS + LDFLAGS + ins + LDLIBS + ['-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=[], LDLIBS=[]):
+ CC = build.vars['CC'][0]
+ LDFLAGS = build.vars['LDFLAGS'][0] + LDFLAGS
+ LDLIBS = build.vars['LDLIBS'][0] + LDLIBS
+ out = 'atto-test'
+
+ args = CC + LDFLAGS + ins + LDLIBS + ['-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=[], LDLIBS=[]):
+ CC = build.vars['CC'][0]
+ CFLAGS = build.vars['CFLAGS'][0] + CFLAGS
+ CPPFLAGS = build.vars['CPPFLAGS'][0] + CPPFLAGS
+ LDFLAGS = build.vars['LDFLAGS'][0] + LDFLAGS
+ LDLIBS = build.vars['LDLIBS'][0] + LDLIBS
+ 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 + LDLIBS + ['-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=[], LDLIBS=[]):
+ CXX = build.vars['CXX'][0]
+ LDFLAGS = build.vars['LDFLAGS'][0] + LDFLAGS
+ LDLIBS = build.vars['LDLIBS'][0] + LDLIBS
+ out = 'atto-test'
+
+ args = CXX + LDFLAGS + ins + LDLIBS + ['-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=[], LDLIBS=[]):
+ CXX = build.vars['CXX'][0]
+ CXXFLAGS = build.vars['CXXFLAGS'][0] + CXXFLAGS
+ CPPFLAGS = build.vars['CPPFLAGS'][0] + CPPFLAGS
+ LDFLAGS = build.vars['LDFLAGS'][0] + LDFLAGS
+ LDLIBS = build.vars['LDLIBS'][0] + LDLIBS
+ 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 + LDLIBS + ['-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):
+ # Make expects something different
+ build.vars['LDLIBS'] = build.vars['LIBS']
+ del build.vars['LIBS']
+
+def cppflags(build, CPPFLAGS):
+ pass
+
+def cc(build, CC):
+ pass
+
+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):
+ pass
+
+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=shell, 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=shell, check=libs,
+ help='libraries to pass to the linker, e.g. -l<library>',
+ hidden=False)
+
+class Preprocess(Arches2):
+ __slots__ = ()
+ def vars(self):
+ super(Preprocess, self).vars()
+ self.add_option('CPPFLAGS', init=[],
+ type=shell, 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=['gcc'],
+ type=shell, check=cc,
+ help='C compiler command', hidden=False)
+ self.add_option('CFLAGS', init=[],
+ type=shell, 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=['g++'],
+ type=shell, check=cxx,
+ help='C++ compiler command', hidden=False)
+ self.add_option('CXXFLAGS', init=[],
+ type=shell, check=cxxflags,
+ help='C++ compiler flags', hidden=False)
diff --git a/attoconf/lib/install.py b/attoconf/lib/install.py
new file mode 100644
index 0000000..3e135e8
--- /dev/null
+++ b/attoconf/lib/install.py
@@ -0,0 +1,320 @@
+# 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 __future__ import print_function, division, absolute_import
+
+import os
+
+from ..classy import ClassyProject
+from ..core import trim_trailing_slashes
+
+
+def word(s):
+ if ' ' in s:
+ raise ValueError('not a word: %s' % s)
+ return s
+
+
+def version(s):
+ if s.startswith('v'):
+ s = s[1:]
+ [int(b) for b in s.split('.')]
+ return s
+
+
+def filepath(s):
+ s = trim_trailing_slashes(s)
+ # must be absolute *and* canonical
+ if s != os.path.abspath(s):
+ raise ValueError('Not an absolute, canonical pathname: %s' % s)
+ return s
+
+
+def exec_prefix(build, EPREFIX):
+ if EPREFIX is None:
+ PREFIX, origin = build.vars['PREFIX']
+ if origin != 'default':
+ origin = 'derived from PREFIX'
+ build.vars['EXEC_PREFIX'] = (PREFIX, origin)
+ # is this a good idea? is there a better way?
+ # how will this interfere with hashing?
+ # at least we don't have to worry about the environment ...
+ build.vars['EPREFIX'] = build.vars['EXEC_PREFIX']
+ del build.vars['EXEC_PREFIX']
+
+def bindir(build, DIR):
+ if DIR is None:
+ EPREFIX, origin = build.vars['EPREFIX']
+ if origin != 'default':
+ origin = 'derived from EPREFIX'
+ build.vars['BINDIR'] = (os.path.join(EPREFIX, 'bin'), origin)
+
+def sbindir(build, DIR):
+ if DIR is None:
+ EPREFIX, origin = build.vars['EPREFIX']
+ if origin != 'default':
+ origin = 'derived from EPREFIX'
+ build.vars['SBINDIR'] = (os.path.join(EPREFIX, 'sbin'), origin)
+
+def libexecdir(build, DIR):
+ if DIR is None:
+ EPREFIX, origin = build.vars['EPREFIX']
+ if origin != 'default':
+ origin = 'derived from EPREFIX'
+ build.vars['LIBEXECDIR'] = (os.path.join(EPREFIX, 'libexec'), origin)
+
+def sysconfdir(build, DIR):
+ if DIR is None:
+ PREFIX, origin = build.vars['PREFIX']
+ if origin != 'default':
+ origin = 'derived from PREFIX'
+ build.vars['SYSCONFDIR'] = (os.path.join(PREFIX, 'etc'), origin)
+
+def sharedstatedir(build, DIR):
+ if DIR is None:
+ PREFIX, origin = build.vars['PREFIX']
+ if origin != 'default':
+ origin = 'derived from PREFIX'
+ build.vars['SHAREDSTATEDIR'] = (os.path.join(PREFIX, 'com'), origin)
+
+def localstatedir(build, DIR):
+ if DIR is None:
+ PREFIX, origin = build.vars['PREFIX']
+ if origin != 'default':
+ origin = 'derived from PREFIX'
+ build.vars['LOCALSTATEDIR'] = (os.path.join(PREFIX, 'var'), origin)
+
+def libdir(build, DIR):
+ if DIR is None:
+ EPREFIX, origin = build.vars['EPREFIX']
+ if origin != 'default':
+ origin = 'derived from EPREFIX'
+ build.vars['LIBDIR'] = (os.path.join(EPREFIX, 'lib'), origin)
+
+def includedir(build, DIR):
+ if DIR is None:
+ PREFIX, origin = build.vars['PREFIX']
+ if origin != 'default':
+ origin = 'derived from PREFIX'
+ build.vars['INCLUDEDIR'] = (os.path.join(PREFIX, 'include'), origin)
+
+def datarootdir(build, DIR):
+ if DIR is None:
+ PREFIX, origin = build.vars['PREFIX']
+ if origin != 'default':
+ origin = 'derived from PREFIX'
+ build.vars['DATAROOTDIR'] = (os.path.join(PREFIX, 'share'), origin)
+
+def datadir(build, DIR):
+ if DIR is None:
+ DATAROOTDIR, origin = build.vars['DATAROOTDIR']
+ if origin != 'default':
+ origin = 'derived from DATAROOTDIR'
+ build.vars['DATADIR'] = (DATAROOTDIR, origin)
+
+def packagedatadir(build, DIR):
+ if DIR is None:
+ DATADIR, origin = build.vars['DATADIR']
+ PACKAGE, prigin = build.vars['PACKAGE']
+ if origin != 'default' or prigin != 'default':
+ origin = 'derived from DATADIR and PACKAGE'
+ build.vars['DATADIR'] = (os.path.join(DATADIR, PACKAGE), origin)
+
+def infodir(build, DIR):
+ if DIR is None:
+ DATAROOTDIR, origin = build.vars['DATAROOTDIR']
+ if origin != 'default':
+ origin = 'derived from DATAROOTDIR'
+ build.vars['INFODIR'] = (os.path.join(DATAROOTDIR, 'info'), origin)
+
+def localedir(build, DIR):
+ if DIR is None:
+ DATAROOTDIR, origin = build.vars['DATAROOTDIR']
+ if origin != 'default':
+ origin = 'derived from DATAROOTDIR'
+ build.vars['LOCALEDIR'] = (os.path.join(DATAROOTDIR, 'locale'), origin)
+
+def mandir(build, DIR):
+ if DIR is None:
+ DATAROOTDIR, origin = build.vars['DATAROOTDIR']
+ if origin != 'default':
+ origin = 'derived from DATAROOTDIR'
+ build.vars['MANDIR'] = (os.path.join(DATAROOTDIR, 'man'), origin)
+
+def docdir(build, DIR):
+ if DIR is None:
+ DATAROOTDIR, origin = build.vars['DATAROOTDIR']
+ PACKAGE, origin2 = build.vars['PACKAGE']
+ if origin != 'default' or origin2 != 'default':
+ origin = 'derived from DATAROOTDIR and PACKAGE'
+ build.vars['DOCDIR'] = (os.path.join(DATAROOTDIR, 'doc', PACKAGE), origin)
+
+def htmldir(build, DIR):
+ if DIR is None:
+ DOCDIR, origin = build.vars['DOCDIR']
+ if origin != 'default':
+ origin = 'derived from DOCDIR'
+ build.vars['HTMLDIR'] = (DOCDIR, origin)
+
+def dvidir(build, DIR):
+ if DIR is None:
+ DOCDIR, origin = build.vars['DOCDIR']
+ if origin != 'default':
+ origin = 'derived from DOCDIR'
+ build.vars['DVIDIR'] = (DOCDIR, origin)
+
+def pdfdir(build, DIR):
+ if DIR is None:
+ DOCDIR, origin = build.vars['DOCDIR']
+ if origin != 'default':
+ origin = 'derived from DOCDIR'
+ build.vars['PDFDIR'] = (DOCDIR, origin)
+
+def psdir(build, DIR):
+ if DIR is None:
+ DOCDIR, origin = build.vars['DOCDIR']
+ if origin != 'default':
+ origin = 'derived from DOCDIR'
+ build.vars['PSDIR'] = (DOCDIR, origin)
+
+
+class Install(ClassyProject):
+ __slots__ = ()
+
+ @classmethod
+ def slots(cls):
+ return super(Install, cls).slots() + (
+ 'package', 'package_version', 'package_name')
+
+ def set_package(self, package, version, name):
+ self.package = package
+ self.package_version = version
+ self.package_name = name
+
+ def general(self):
+ super(Install, self).general()
+ self.add_option('--package', init=self.package,
+ type=word, check=None,
+ help='Short name of this package (don\'t change!)',
+ hidden=True)
+ self.add_option('--package-version', init=self.package_version,
+ type=version, check=None,
+ help='Version of this package (change in configure)',
+ hidden=True,
+ help_var='VERSION')
+ self.add_option('--package-name', init=self.package_name,
+ type=version, check=None,
+ 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=None,
+ help='install architecture-independent files in PREFIX',
+ hidden=False)
+ self.add_option('--exec-prefix', init=None,
+ type=filepath, check=exec_prefix,
+ help='install architecture-dependent files in EPREFIX',
+ hidden=False,
+ help_var='EPREFIX', help_def='PREFIX')
+
+ self.add_help('Fine tuning of the installation directories:',
+ hidden=False)
+ self.add_option('--bindir', init=None,
+ type=filepath, check=bindir,
+ help='user executables', hidden=False,
+ help_var='DIR', help_def='EPREFIX/bin')
+ self.add_option('--sbindir', init=None,
+ type=filepath, check=sbindir,
+ help='system admin executables', hidden=False,
+ help_var='DIR', help_def='EPREFIX/sbin')
+ self.add_option('--libexecdir', init=None,
+ type=filepath, check=libexecdir,
+ help='program executables', hidden=False,
+ help_var='DIR', help_def='EPREFIX/libexec')
+ self.add_option('--sysconfdir', init=None,
+ type=filepath, check=sysconfdir,
+ help='read-only single-machine data', hidden=False,
+ help_var='DIR', help_def='PREFIX/etc')
+ self.add_option('--sharedstatedir', init=None,
+ type=filepath, check=sharedstatedir,
+ help='modifiable architecture-independent data', hidden=False,
+ help_var='DIR', help_def='PREFIX/com')
+ self.add_option('--localstatedir', init=None,
+ type=filepath, check=localstatedir,
+ help='modifiable single-machine data', hidden=False,
+ help_var='DIR', help_def='PREFIX/var')
+ self.add_option('--libdir', init=None,
+ type=filepath, check=libdir,
+ help='object code libraries', hidden=False,
+ help_var='DIR', help_def='EPREFIX/lib')
+ self.add_option('--includedir', init=None,
+ type=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=None,
+ help='C header files for non-gcc', hidden=False,
+ help_var='DIR')
+ self.add_option('--datarootdir', init=None,
+ type=filepath, check=datarootdir,
+ help='read-only arch.-independent data root', hidden=False,
+ help_var='DIR', help_def='PREFIX/share')
+ self.add_option('--datadir', init=None,
+ type=filepath, check=datadir,
+ help='read-only architecture-independent data', hidden=False,
+ help_var='DIR', help_def='DATAROOTDIR')
+ self.add_option('--packagedatadir', init=None,
+ type=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=None,
+ type=filepath, check=infodir,
+ help='info documentation', hidden=False,
+ help_var='DIR', help_def='DATAROOTDIR/info')
+ self.add_option('--localedir', init=None,
+ type=filepath, check=localedir,
+ help='locale-dependent data', hidden=False,
+ help_var='DIR', help_def='DATAROOTDIR/locale')
+ self.add_option('--mandir', init=None,
+ type=filepath, check=mandir,
+ help='man documentation', hidden=False,
+ help_var='DIR', help_def='DATAROOTDIR/man')
+ self.add_option('--docdir', init=None,
+ type=filepath, check=docdir,
+ help='documentation root', hidden=False,
+ help_var='DIR', help_def='DATAROOTDIR/doc/PACKAGE')
+ self.add_option('--htmldir', init=None,
+ type=filepath, check=htmldir,
+ help='html documentation', hidden=False,
+ help_var='DIR', help_def='DOCDIR')
+ self.add_option('--dvidir', init=None,
+ type=filepath, check=dvidir,
+ help='dvi documentation', hidden=False,
+ help_var='DIR', help_def='DOCDIR')
+ self.add_option('--pdfdir', init=None,
+ type=filepath, check=pdfdir,
+ help='pdf documentation', hidden=False,
+ help_var='DIR', help_def='DOCDIR')
+ self.add_option('--psdir', init=None,
+ type=filepath, check=psdir,
+ help='ps documentation', hidden=False,
+ help_var='DIR', help_def='DOCDIR')
diff --git a/attoconf/lib/make.py b/attoconf/lib/make.py
new file mode 100644
index 0000000..e54224d
--- /dev/null
+++ b/attoconf/lib/make.py
@@ -0,0 +1,94 @@
+# 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 __future__ import print_function, division, absolute_import
+
+import os
+from pipes import quote
+
+from ..classy import ClassyProject
+from ..version import string as version_string
+
+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' % version_string)
+ out.write('SRC_DIR = %s\n' % build.relative_source())
+ out.write('\n')
+ # TODO preserve *original* order?
+ for var, (val, origin) in sorted(build.vars.iteritems()):
+ if val is None:
+ if origin == 'default':
+ continue
+ # is it a good idea for Nones to survive this long?
+ # especially conditional ones ...
+ var = '# ' + var
+ val = 'not defined'
+ elif isinstance(val, list):
+ val = ' '.join(quote(a) for a in val)
+ elif isinstance(val, str):
+ val = quote(val)
+ else:
+ print('Assuming it\'s safe to print an instance of',
+ type(val).__name__, '...')
+ out.write('%s = %s # %s\n' % (var, val, origin))
+ if self.infile is not None:
+ out.write('\n# 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__ = ()
+ @classmethod
+ def slots(cls):
+ return super(Make, cls).slots() + ('make_in', 'make_out')
+
+ def __init__(self, srcdir):
+ super(Make, self).__init__(srcdir)
+ self.set_make_infile('Makefile.in')
+ self.set_make_outfile('Makefile') # 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()
+ self.checks.append(MakeHook(self.make_in, self.make_out))