# Copyright 2013-2014 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 . 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 if you have libraries in a nonstandard directory ', hidden=False) self.add_option('LIBS', init=[], type=ShellList, check=libs, help='libraries to pass to the linker, e.g. -l', 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 if you have headers in a nonstandard directory ', 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)