summaryrefslogtreecommitdiff
path: root/third_party/waf/wafadmin/Tools/ccroot.py
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/waf/wafadmin/Tools/ccroot.py')
-rw-r--r--third_party/waf/wafadmin/Tools/ccroot.py639
1 files changed, 639 insertions, 0 deletions
diff --git a/third_party/waf/wafadmin/Tools/ccroot.py b/third_party/waf/wafadmin/Tools/ccroot.py
new file mode 100644
index 00000000000..c130b40a21d
--- /dev/null
+++ b/third_party/waf/wafadmin/Tools/ccroot.py
@@ -0,0 +1,639 @@
+#!/usr/bin/env python
+# encoding: utf-8
+# Thomas Nagy, 2005-2008 (ita)
+
+"base for all c/c++ programs and libraries"
+
+import os, sys, re
+import TaskGen, Task, Utils, preproc, Logs, Build, Options
+from Logs import error, debug, warn
+from Utils import md5
+from TaskGen import taskgen, after, before, feature
+from Constants import *
+from Configure import conftest
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from io import StringIO
+
+import config_c # <- necessary for the configuration, do not touch
+
+USE_TOP_LEVEL = False
+
+def get_cc_version(conf, cc, gcc=False, icc=False):
+
+ cmd = cc + ['-dM', '-E', '-']
+ try:
+ p = Utils.pproc.Popen(cmd, stdin=Utils.pproc.PIPE, stdout=Utils.pproc.PIPE, stderr=Utils.pproc.PIPE)
+ p.stdin.write('\n')
+ out = p.communicate()[0]
+ except:
+ conf.fatal('could not determine the compiler version %r' % cmd)
+
+ # PY3K: do not touch
+ out = str(out)
+
+ if gcc:
+ if out.find('__INTEL_COMPILER') >= 0:
+ conf.fatal('The intel compiler pretends to be gcc')
+ if out.find('__GNUC__') < 0:
+ conf.fatal('Could not determine the compiler type')
+
+ if icc and out.find('__INTEL_COMPILER') < 0:
+ conf.fatal('Not icc/icpc')
+
+ k = {}
+ if icc or gcc:
+ out = out.split('\n')
+ import shlex
+
+ for line in out:
+ lst = shlex.split(line)
+ if len(lst)>2:
+ key = lst[1]
+ val = lst[2]
+ k[key] = val
+
+ def isD(var):
+ return var in k
+
+ def isT(var):
+ return var in k and k[var] != '0'
+
+ # Some documentation is available at http://predef.sourceforge.net
+ # The names given to DEST_OS must match what Utils.unversioned_sys_platform() returns.
+ mp1 = {
+ '__linux__' : 'linux',
+ '__GNU__' : 'gnu',
+ '__FreeBSD__' : 'freebsd',
+ '__NetBSD__' : 'netbsd',
+ '__OpenBSD__' : 'openbsd',
+ '__sun' : 'sunos',
+ '__hpux' : 'hpux',
+ '__sgi' : 'irix',
+ '_AIX' : 'aix',
+ '__CYGWIN__' : 'cygwin',
+ '__MSYS__' : 'msys',
+ '_UWIN' : 'uwin',
+ '_WIN64' : 'win32',
+ '_WIN32' : 'win32',
+ '__POWERPC__' : 'powerpc',
+ }
+
+ for i in mp1:
+ if isD(i):
+ conf.env.DEST_OS = mp1[i]
+ break
+ else:
+ if isD('__APPLE__') and isD('__MACH__'):
+ conf.env.DEST_OS = 'darwin'
+ elif isD('__unix__'): # unix must be tested last as it's a generic fallback
+ conf.env.DEST_OS = 'generic'
+
+ if isD('__ELF__'):
+ conf.env.DEST_BINFMT = 'elf'
+ elif isD('__WINNT__') or isD('__CYGWIN__'):
+ conf.env.DEST_BINFMT = 'pe'
+ elif isD('__APPLE__'):
+ conf.env.DEST_BINFMT = 'mac-o'
+
+ mp2 = {
+ '__x86_64__' : 'x86_64',
+ '__i386__' : 'x86',
+ '__ia64__' : 'ia',
+ '__mips__' : 'mips',
+ '__sparc__' : 'sparc',
+ '__alpha__' : 'alpha',
+ '__arm__' : 'arm',
+ '__hppa__' : 'hppa',
+ '__powerpc__' : 'powerpc',
+ }
+ for i in mp2:
+ if isD(i):
+ conf.env.DEST_CPU = mp2[i]
+ break
+
+ debug('ccroot: dest platform: ' + ' '.join([conf.env[x] or '?' for x in ('DEST_OS', 'DEST_BINFMT', 'DEST_CPU')]))
+ conf.env['CC_VERSION'] = (k['__GNUC__'], k['__GNUC_MINOR__'], k['__GNUC_PATCHLEVEL__'])
+ return k
+
+class DEBUG_LEVELS:
+ """Will disappear in waf 1.6"""
+ ULTRADEBUG = "ultradebug"
+ DEBUG = "debug"
+ RELEASE = "release"
+ OPTIMIZED = "optimized"
+ CUSTOM = "custom"
+
+ ALL = [ULTRADEBUG, DEBUG, RELEASE, OPTIMIZED, CUSTOM]
+
+def scan(self):
+ "look for .h the .cpp need"
+ debug('ccroot: _scan_preprocessor(self, node, env, path_lst)')
+
+ # TODO waf 1.6 - assume the default input has exactly one file
+
+ if len(self.inputs) == 1:
+ node = self.inputs[0]
+ (nodes, names) = preproc.get_deps(node, self.env, nodepaths = self.env['INC_PATHS'])
+ if Logs.verbose:
+ debug('deps: deps for %s: %r; unresolved %r', str(node), nodes, names)
+ return (nodes, names)
+
+ all_nodes = []
+ all_names = []
+ seen = set()
+ for node in self.inputs:
+ (nodes, names) = preproc.get_deps(node, self.env, nodepaths = self.env['INC_PATHS'])
+ if Logs.verbose:
+ debug('deps: deps for %s: %r; unresolved %r', str(node), nodes, names)
+ for x in nodes:
+ if id(x) in seen: continue
+ seen.add(id(x))
+ all_nodes.append(x)
+ for x in names:
+ if not x in all_names:
+ all_names.append(x)
+ return (all_nodes, all_names)
+
+class ccroot_abstract(TaskGen.task_gen):
+ "Parent class for programs and libraries in languages c, c++ and moc (Qt)"
+ def __init__(self, *k, **kw):
+ # COMPAT remove in waf 1.6 TODO
+ if len(k) > 1:
+ k = list(k)
+ if k[1][0] != 'c':
+ k[1] = 'c' + k[1]
+ TaskGen.task_gen.__init__(self, *k, **kw)
+
+def get_target_name(self):
+ tp = 'program'
+ for x in self.features:
+ if x in ['cshlib', 'cstaticlib']:
+ tp = x.lstrip('c')
+
+ pattern = self.env[tp + '_PATTERN']
+ if not pattern: pattern = '%s'
+
+ dir, name = os.path.split(self.target)
+
+ if 'cshlib' in self.features and getattr(self, 'vnum', None):
+ nums = self.vnum.split('.')
+ if self.env.DEST_BINFMT == 'pe':
+ # include the version in the dll file name,
+ # the import lib file name stays unversionned.
+ name = name + '-' + nums[0]
+ elif self.env.DEST_OS == 'openbsd':
+ pattern = '%s.%s' % (pattern, nums[0])
+ if len(nums) >= 2:
+ pattern += '.%s' % nums[1]
+
+ return os.path.join(dir, pattern % name)
+
+@feature('cc', 'cxx')
+@before('apply_core')
+def default_cc(self):
+ """compiled_tasks attribute must be set before the '.c->.o' tasks can be created"""
+ Utils.def_attrs(self,
+ includes = '',
+ defines= '',
+ rpaths = '',
+ uselib = '',
+ uselib_local = '',
+ add_objects = '',
+ p_flag_vars = [],
+ p_type_vars = [],
+ compiled_tasks = [],
+ link_task = None)
+
+ # The only thing we need for cross-compilation is DEST_BINFMT.
+ # At some point, we may reach a case where DEST_BINFMT is not enough, but for now it's sufficient.
+ # Currently, cross-compilation is auto-detected only for the gnu and intel compilers.
+ if not self.env.DEST_BINFMT:
+ # Infer the binary format from the os name.
+ self.env.DEST_BINFMT = Utils.unversioned_sys_platform_to_binary_format(
+ self.env.DEST_OS or Utils.unversioned_sys_platform())
+
+ if not self.env.BINDIR: self.env.BINDIR = Utils.subst_vars('${PREFIX}/bin', self.env)
+ if not self.env.LIBDIR: self.env.LIBDIR = Utils.subst_vars('${PREFIX}/lib${LIB_EXT}', self.env)
+
+@feature('cprogram', 'dprogram', 'cstaticlib', 'dstaticlib', 'cshlib', 'dshlib')
+def apply_verif(self):
+ """no particular order, used for diagnostic"""
+ if not (self.source or getattr(self, 'add_objects', None) or getattr(self, 'uselib_local', None) or getattr(self, 'obj_files', None)):
+ raise Utils.WafError('no source files specified for %s' % self)
+ if not self.target:
+ raise Utils.WafError('no target for %s' % self)
+
+# TODO reference the d programs, shlibs in d.py, not here
+
+@feature('cprogram', 'dprogram')
+@after('default_cc')
+@before('apply_core')
+def vars_target_cprogram(self):
+ self.default_install_path = self.env.BINDIR
+ self.default_chmod = O755
+
+@after('default_cc')
+@feature('cshlib', 'dshlib')
+@before('apply_core')
+def vars_target_cshlib(self):
+ if self.env.DEST_BINFMT == 'pe':
+ # set execute bit on libs to avoid 'permission denied' (issue 283)
+ self.default_chmod = O755
+ self.default_install_path = self.env.BINDIR
+ else:
+ self.default_install_path = self.env.LIBDIR
+
+@feature('cprogram', 'dprogram', 'cstaticlib', 'dstaticlib', 'cshlib', 'dshlib')
+@after('apply_link', 'vars_target_cprogram', 'vars_target_cshlib')
+def default_link_install(self):
+ """you may kill this method to inject your own installation for the first element
+ any other install should only process its own nodes and not those from the others"""
+ if self.install_path:
+ self.bld.install_files(self.install_path, self.link_task.outputs[0], env=self.env, chmod=self.chmod)
+
+@feature('cc', 'cxx')
+@after('apply_type_vars', 'apply_lib_vars', 'apply_core')
+def apply_incpaths(self):
+ """used by the scanner
+ after processing the uselib for CPPPATH
+ after apply_core because some processing may add include paths
+ """
+ lst = []
+ # TODO move the uselib processing out of here
+ for lib in self.to_list(self.uselib):
+ for path in self.env['CPPPATH_' + lib]:
+ if not path in lst:
+ lst.append(path)
+ if preproc.go_absolute:
+ for path in preproc.standard_includes:
+ if not path in lst:
+ lst.append(path)
+
+ for path in self.to_list(self.includes):
+ if not path in lst:
+ if preproc.go_absolute or not os.path.isabs(path):
+ lst.append(path)
+ else:
+ self.env.prepend_value('CPPPATH', path)
+
+ for path in lst:
+ node = None
+ if os.path.isabs(path):
+ if preproc.go_absolute:
+ node = self.bld.root.find_dir(path)
+ elif path[0] == '#':
+ node = self.bld.srcnode
+ if len(path) > 1:
+ node = node.find_dir(path[1:])
+ else:
+ node = self.path.find_dir(path)
+
+ if node:
+ self.env.append_value('INC_PATHS', node)
+
+ # TODO WAF 1.6
+ if USE_TOP_LEVEL:
+ self.env.append_value('INC_PATHS', self.bld.srcnode)
+
+@feature('cc', 'cxx')
+@after('init_cc', 'init_cxx')
+@before('apply_lib_vars')
+def apply_type_vars(self):
+ """before apply_lib_vars because we modify uselib
+ after init_cc and init_cxx because web need p_type_vars
+ """
+ for x in self.features:
+ if not x in ['cprogram', 'cstaticlib', 'cshlib']:
+ continue
+ x = x.lstrip('c')
+
+ # if the type defines uselib to add, add them
+ st = self.env[x + '_USELIB']
+ if st: self.uselib = self.uselib + ' ' + st
+
+ # each compiler defines variables like 'shlib_CXXFLAGS', 'shlib_LINKFLAGS', etc
+ # so when we make a task generator of the type shlib, CXXFLAGS are modified accordingly
+ for var in self.p_type_vars:
+ compvar = '%s_%s' % (x, var)
+ #print compvar
+ value = self.env[compvar]
+ if value: self.env.append_value(var, value)
+
+@feature('cprogram', 'cshlib', 'cstaticlib')
+@after('apply_core')
+def apply_link(self):
+ """executes after apply_core for collecting 'compiled_tasks'
+ use a custom linker if specified (self.link='name-of-custom-link-task')"""
+ link = getattr(self, 'link', None)
+ if not link:
+ if 'cstaticlib' in self.features: link = 'static_link'
+ elif 'cxx' in self.features: link = 'cxx_link'
+ else: link = 'cc_link'
+
+ tsk = self.create_task(link)
+ outputs = [t.outputs[0] for t in self.compiled_tasks]
+ tsk.set_inputs(outputs)
+ tsk.set_outputs(self.path.find_or_declare(get_target_name(self)))
+
+ self.link_task = tsk
+
+@feature('cc', 'cxx')
+@after('apply_link', 'init_cc', 'init_cxx', 'apply_core')
+def apply_lib_vars(self):
+ """after apply_link because of 'link_task'
+ after default_cc because of the attribute 'uselib'"""
+
+ # after 'apply_core' in case if 'cc' if there is no link
+
+ env = self.env
+
+ # 1. the case of the libs defined in the project (visit ancestors first)
+ # the ancestors external libraries (uselib) will be prepended
+ self.uselib = self.to_list(self.uselib)
+ names = self.to_list(self.uselib_local)
+
+ seen = set([])
+ tmp = Utils.deque(names) # consume a copy of the list of names
+ while tmp:
+ lib_name = tmp.popleft()
+ # visit dependencies only once
+ if lib_name in seen:
+ continue
+
+ y = self.name_to_obj(lib_name)
+ if not y:
+ raise Utils.WafError('object %r was not found in uselib_local (required by %r)' % (lib_name, self.name))
+ y.post()
+ seen.add(lib_name)
+
+ # object has ancestors to process (shared libraries): add them to the end of the list
+ if getattr(y, 'uselib_local', None):
+ lst = y.to_list(y.uselib_local)
+ if 'cshlib' in y.features or 'cprogram' in y.features:
+ lst = [x for x in lst if not 'cstaticlib' in self.name_to_obj(x).features]
+ tmp.extend(lst)
+
+ # link task and flags
+ if getattr(y, 'link_task', None):
+
+ link_name = y.target[y.target.rfind(os.sep) + 1:]
+ if 'cstaticlib' in y.features:
+ env.append_value('STATICLIB', link_name)
+ elif 'cshlib' in y.features or 'cprogram' in y.features:
+ # WARNING some linkers can link against programs
+ env.append_value('LIB', link_name)
+
+ # the order
+ self.link_task.set_run_after(y.link_task)
+
+ # for the recompilation
+ dep_nodes = getattr(self.link_task, 'dep_nodes', [])
+ self.link_task.dep_nodes = dep_nodes + y.link_task.outputs
+
+ # add the link path too
+ tmp_path = y.link_task.outputs[0].parent.bldpath(self.env)
+ if not tmp_path in env['LIBPATH']: env.prepend_value('LIBPATH', tmp_path)
+
+ # add ancestors uselib too - but only propagate those that have no staticlib
+ for v in self.to_list(y.uselib):
+ if not env['STATICLIB_' + v]:
+ if not v in self.uselib:
+ self.uselib.insert(0, v)
+
+ # if the library task generator provides 'export_incdirs', add to the include path
+ # the export_incdirs must be a list of paths relative to the other library
+ if getattr(y, 'export_incdirs', None):
+ for x in self.to_list(y.export_incdirs):
+ node = y.path.find_dir(x)
+ if not node:
+ raise Utils.WafError('object %r: invalid folder %r in export_incdirs' % (y.target, x))
+ self.env.append_unique('INC_PATHS', node)
+
+ # 2. the case of the libs defined outside
+ for x in self.uselib:
+ for v in self.p_flag_vars:
+ val = self.env[v + '_' + x]
+ if val: self.env.append_value(v, val)
+
+@feature('cprogram', 'cstaticlib', 'cshlib')
+@after('init_cc', 'init_cxx', 'apply_link')
+def apply_objdeps(self):
+ "add the .o files produced by some other object files in the same manner as uselib_local"
+ if not getattr(self, 'add_objects', None): return
+
+ seen = []
+ names = self.to_list(self.add_objects)
+ while names:
+ x = names[0]
+
+ # visit dependencies only once
+ if x in seen:
+ names = names[1:]
+ continue
+
+ # object does not exist ?
+ y = self.name_to_obj(x)
+ if not y:
+ raise Utils.WafError('object %r was not found in uselib_local (required by add_objects %r)' % (x, self.name))
+
+ # object has ancestors to process first ? update the list of names
+ if getattr(y, 'add_objects', None):
+ added = 0
+ lst = y.to_list(y.add_objects)
+ lst.reverse()
+ for u in lst:
+ if u in seen: continue
+ added = 1
+ names = [u]+names
+ if added: continue # list of names modified, loop
+
+ # safe to process the current object
+ y.post()
+ seen.append(x)
+
+ for t in y.compiled_tasks:
+ self.link_task.inputs.extend(t.outputs)
+
+@feature('cprogram', 'cshlib', 'cstaticlib')
+@after('apply_lib_vars')
+def apply_obj_vars(self):
+ """after apply_lib_vars for uselib"""
+ v = self.env
+ lib_st = v['LIB_ST']
+ staticlib_st = v['STATICLIB_ST']
+ libpath_st = v['LIBPATH_ST']
+ staticlibpath_st = v['STATICLIBPATH_ST']
+ rpath_st = v['RPATH_ST']
+
+ app = v.append_unique
+
+ if v['FULLSTATIC']:
+ v.append_value('LINKFLAGS', v['FULLSTATIC_MARKER'])
+
+ for i in v['RPATH']:
+ if i and rpath_st:
+ app('LINKFLAGS', rpath_st % i)
+
+ for i in v['LIBPATH']:
+ app('LINKFLAGS', libpath_st % i)
+ app('LINKFLAGS', staticlibpath_st % i)
+
+ if v['STATICLIB']:
+ v.append_value('LINKFLAGS', v['STATICLIB_MARKER'])
+ k = [(staticlib_st % i) for i in v['STATICLIB']]
+ app('LINKFLAGS', k)
+
+ # fully static binaries ?
+ if not v['FULLSTATIC']:
+ if v['STATICLIB'] or v['LIB']:
+ v.append_value('LINKFLAGS', v['SHLIB_MARKER'])
+
+ app('LINKFLAGS', [lib_st % i for i in v['LIB']])
+
+@after('apply_link')
+def process_obj_files(self):
+ if not hasattr(self, 'obj_files'): return
+ for x in self.obj_files:
+ node = self.path.find_resource(x)
+ self.link_task.inputs.append(node)
+
+@taskgen
+def add_obj_file(self, file):
+ """Small example on how to link object files as if they were source
+ obj = bld.create_obj('cc')
+ obj.add_obj_file('foo.o')"""
+ if not hasattr(self, 'obj_files'): self.obj_files = []
+ if not 'process_obj_files' in self.meths: self.meths.append('process_obj_files')
+ self.obj_files.append(file)
+
+c_attrs = {
+'cxxflag' : 'CXXFLAGS',
+'cflag' : 'CCFLAGS',
+'ccflag' : 'CCFLAGS',
+'linkflag' : 'LINKFLAGS',
+'ldflag' : 'LINKFLAGS',
+'lib' : 'LIB',
+'libpath' : 'LIBPATH',
+'staticlib': 'STATICLIB',
+'staticlibpath': 'STATICLIBPATH',
+'rpath' : 'RPATH',
+'framework' : 'FRAMEWORK',
+'frameworkpath' : 'FRAMEWORKPATH'
+}
+
+@feature('cc', 'cxx')
+@before('init_cxx', 'init_cc')
+@before('apply_lib_vars', 'apply_obj_vars', 'apply_incpaths', 'init_cc')
+def add_extra_flags(self):
+ """case and plural insensitive
+ before apply_obj_vars for processing the library attributes
+ """
+ for x in self.__dict__.keys():
+ y = x.lower()
+ if y[-1] == 's':
+ y = y[:-1]
+ if c_attrs.get(y, None):
+ self.env.append_unique(c_attrs[y], getattr(self, x))
+
+# ============ the code above must not know anything about import libs ==========
+
+@feature('cshlib')
+@after('apply_link', 'default_cc')
+@before('apply_lib_vars', 'apply_objdeps', 'default_link_install')
+def apply_implib(self):
+ """On mswindows, handle dlls and their import libs
+ the .dll.a is the import lib and it is required for linking so it is installed too
+ """
+ if not self.env.DEST_BINFMT == 'pe':
+ return
+
+ self.meths.remove('default_link_install')
+
+ bindir = self.install_path
+ if not bindir: return
+
+ # install the dll in the bin dir
+ dll = self.link_task.outputs[0]
+ self.bld.install_files(bindir, dll, self.env, self.chmod)
+
+ # add linker flags to generate the import lib
+ implib = self.env['implib_PATTERN'] % os.path.split(self.target)[1]
+
+ implib = dll.parent.find_or_declare(implib)
+ self.link_task.outputs.append(implib)
+ self.bld.install_as('${LIBDIR}/%s' % implib.name, implib, self.env)
+
+ self.env.append_value('LINKFLAGS', (self.env['IMPLIB_ST'] % implib.bldpath(self.env)).split())
+
+# ============ the code above must not know anything about vnum processing on unix platforms =========
+
+@feature('cshlib')
+@after('apply_link')
+@before('apply_lib_vars', 'default_link_install')
+def apply_vnum(self):
+ """
+ libfoo.so is installed as libfoo.so.1.2.3
+ """
+ if not getattr(self, 'vnum', '') or not 'cshlib' in self.features or os.name != 'posix' or self.env.DEST_BINFMT not in ('elf', 'mac-o'):
+ return
+
+ self.meths.remove('default_link_install')
+
+ link = self.link_task
+ nums = self.vnum.split('.')
+ node = link.outputs[0]
+
+ libname = node.name
+ if libname.endswith('.dylib'):
+ name3 = libname.replace('.dylib', '.%s.dylib' % self.vnum)
+ name2 = libname.replace('.dylib', '.%s.dylib' % nums[0])
+ else:
+ name3 = libname + '.' + self.vnum
+ name2 = libname + '.' + nums[0]
+
+ if self.env.SONAME_ST:
+ v = self.env.SONAME_ST % name2
+ self.env.append_value('LINKFLAGS', v.split())
+
+ bld = self.bld
+ nums = self.vnum.split('.')
+
+ path = self.install_path
+ if not path: return
+
+ if self.env.DEST_OS == 'openbsd':
+ libname = self.link_task.outputs[0].name
+ bld.install_as('%s%s%s' % (path, os.sep, libname), node, env=self.env)
+ else:
+ bld.install_as(path + os.sep + name3, node, env=self.env)
+ bld.symlink_as(path + os.sep + name2, name3)
+ bld.symlink_as(path + os.sep + libname, name3)
+
+ # the following task is just to enable execution from the build dir :-/
+ if self.env.DEST_OS != 'openbsd':
+ self.create_task('vnum', node, [node.parent.find_or_declare(name2), node.parent.find_or_declare(name3)])
+
+def exec_vnum_link(self):
+ for x in self.outputs:
+ path = x.abspath(self.env)
+ try:
+ os.remove(path)
+ except OSError:
+ pass
+
+ try:
+ os.symlink(self.inputs[0].name, path)
+ except OSError:
+ return 1
+
+cls = Task.task_type_from_func('vnum', func=exec_vnum_link, ext_in='.bin', color='CYAN')
+cls.quiet = 1
+
+# ============ the --as-needed flag should added during the configuration, not at runtime =========
+
+@conftest
+def add_as_needed(conf):
+ if conf.env.DEST_BINFMT == 'elf' and 'gcc' in (conf.env.CXX_NAME, conf.env.CC_NAME):
+ conf.env.append_unique('LINKFLAGS', '--as-needed')