#! /usr/bin/env python # encoding: utf-8 # Thomas Nagy, 2005 (ita) import os, types, sys, re import ccroot import Action, Object, Params, Scan, Common from Params import debug, error, trace, fatal # default priority for link tasks # an even number means link tasks may be parallelized # an odd number is probably the thing to do g_prio_link=101 # default extensions for source files g_src_file_ext = ['.c', '.cpp', '.cc'] def read_la_file(path): sp = re.compile(r'^([^=]+)=\'(.*)\'$') dc={} file = open(path, "r") for line in file.readlines(): try: #print sp.split(line.strip()) _, left, right, _ = sp.split(line.strip()) dc[left]=right except: pass file.close() return dc # fake libtool files fakelibtool_vardeps = ['CXX', 'PREFIX'] def fakelibtool_build(task): # Writes a .la file, used by libtool dest = open(task.m_outputs[0].abspath(), 'w') sname = task.m_inputs[0].m_name fu = dest.write fu("# Generated by ltmain.sh - GNU libtool 1.5.18 - (pwn3d by BKsys II code name WAF)\n") if task.m_env['vnum']: nums=task.m_env['vnum'].split('.') libname = task.m_inputs[0].m_name name3 = libname+'.'+task.m_env['vnum'] name2 = libname+'.'+nums[0] name1 = libname fu("dlname='%s'\n" % name2) strn = " ".join([name3, name2, name1]) fu("library_names='%s'\n" % (strn) ) else: fu("dlname='%s'\n" % sname) fu("library_names='%s %s %s'\n" % (sname, sname, sname) ) fu("old_library=''\n") vars = ' '.join(task.m_env['libtoolvars']+task.m_env['LINKFLAGS']) fu("dependency_libs='%s'\n" % vars) fu("current=0\n") fu("age=0\nrevision=0\ninstalled=yes\nshouldnotlink=no\n") fu("dlopen=''\ndlpreopen=''\n") fu("libdir='%s/lib'\n" % task.m_env['PREFIX']) dest.close() return 0 fakelibtoolact = Action.GenAction('fakelibtool', fakelibtool_vardeps, buildfunc=fakelibtool_build) # Parent class for programs and libraries in languages c, c++ and moc (Qt) class ccroot(Object.genobj): def __init__(self, type='program'): Object.genobj.__init__(self, type) self.env = Params.g_build.m_allenvs['default'].copy() if not self.env['tools']: fatal('no tool selected') self.includes='' self.linkflags='' self.linkpaths='' self.rpaths='' self.uselib='' self.useliblocal='' self.m_linktask=None self.m_deps_linktask=[] # libtool emulation self.want_libtool=0 self.vnum='' self._incpaths_lst=[] self._bld_incpaths_lst=[] self.p_shlib_deps_names=[] self.p_staticlib_deps_names=[] self.p_compiletasks=[] # do not forget to set the following variables in a subclass self.p_flag_vars = [] self.p_type_vars = [] self.m_type_initials = '' global g_src_file_ext self.m_src_file_ext = g_src_file_ext # subclass me def get_valid_types(self): fatal('subclass method get_valid_types of ccroot') # subclass if necessary def find_sources_in_dirs(self, dirnames): lst=[] for name in dirnames.split(): node = self.m_current_path.find_node( name.split(os.sep) ) for file in node.m_files: (base, ext) = os.path.splitext(file.m_name) if ext in self.m_src_file_ext: lst.append( file.relpath(self.m_current_path)[2:] ) self.source = self.source+(" ".join(lst)) # adding some kind of genericity is tricky # subclass this method if it does not suit your needs def apply(self): trace("apply called for "+self.m_type_initials) if not self.m_type in self.get_valid_types(): fatal('Invalid type for object: '+self.m_type_initials) self.apply_lib_vars() self.apply_type_vars() self.apply_obj_vars() self.apply_incpaths() if self.want_libtool: self.apply_libtool() obj_ext = self.env[self.m_type+'_obj_ext'][0] pre = self.m_type_initials # get the list of folders to use by the scanners # all our objects share the same include paths anyway tree = Params.g_build.m_tree dir_lst = { 'path_lst' : self._incpaths_lst } lst = self.source.split() for filename in lst: node = self.m_current_path.find_node( filename.split(os.sep) ) if not node: fatal("source not found: "+filename+" in "+str(self.m_current_path)) base, ext = os.path.splitext(filename) fun = None try: fun = self.env['handlers_'+pre+'obj_'+ext] #print "fun is", 'handlers_cppobj_'+ext, fun except: pass if fun: fun(self, node) continue if tree.needs_rescan(node): tree.rescan(node, Scan.c_scanner, dir_lst) # create the compilation task: cpp or cc task = self.create_task(self.m_type_initials, self.env) task.m_scanner = Scan.c_scanner task.m_scanner_params = dir_lst task.m_inputs = self.file_in(filename) task.m_outputs = self.file_in(base+obj_ext) self.p_compiletasks.append(task) # and after the objects, the remaining is the link step # link in a lower priority (101) so it runs alone (default is 10) global g_prio_link if self.m_type=='staticlib': linktask = self.create_task(pre+'_link_static', self.env, g_prio_link) else: linktask = self.create_task(pre+'_link', self.env, g_prio_link) outputs = [] for t in self.p_compiletasks: outputs.append(t.m_outputs[0]) linktask.m_inputs = outputs linktask.m_outputs = self.file_in(self.get_target_name()) self.m_linktask = linktask if self.m_type != 'program' and self.want_libtool: latask = self.create_task('fakelibtool', self.env, 200) latask.m_inputs = linktask.m_outputs latask.m_outputs = self.file_in(self.get_target_name('.la')) self.m_latask = latask self.apply_libdeps() def get_target_name(self, ext=None): return self.get_library_name(self.target, self.m_type, ext) def get_library_name(self, name, type, ext=None): prefix = self.env[type+'_PREFIX'] suffix = self.env[type+'_SUFFIX'] if ext: suffix = ext if not prefix: prefix='' if not suffix: suffix='' return ''.join([prefix, name, suffix]) def apply_libdeps(self): # for correct dependency handling, we make here one assumption: # the objects that create the libraries we depend on -> they have been created already # TODO : the lookup may have a cost, caching the names in a hashtable may be a good idea # TODO : bad algorithm for obj in Object.g_allobjs: # if the object we depend on is not posted we will force it right now if obj.target in self.p_staticlib_deps_names: if not obj.m_posted: obj.post() self.m_linktask.m_run_after.append(obj.m_linktask) elif obj.target in self.p_shlib_deps_names: if not obj.m_posted: obj.post() self.m_linktask.m_run_after.append(obj.m_linktask) htbl = Params.g_build.m_tree.m_depends_on try: htbl[self.m_linktask.m_outputs[0]] += self.m_deps_linktask except: htbl[self.m_linktask.m_outputs[0]] = self.m_deps_linktask def apply_incpaths(self): inc_lst = self.includes.split() lst = self._incpaths_lst # add the build directory self._incpaths_lst.append( Params.g_build.m_tree.m_bldnode ) # now process the include paths tree = Params.g_build.m_tree for dir in inc_lst: node = self.m_current_path.find_node( dir.split(os.sep) ) if not node: error("node not found dammit") continue lst.append( node ) node2 = tree.get_mirror_node(node) lst.append( node2 ) lst.append( node ) self._bld_incpaths_lst.append(node) self._bld_incpaths_lst.append(node2) # now the nodes are added to self._incpaths_lst def apply_type_vars(self): trace('apply_type_vars called') for var in self.p_type_vars: # each compiler defines variables like 'shlib_CXXFLAGS', 'shlib_LINKFLAGS', etc # so when we make a cppobj of the type shlib, CXXFLAGS are modified accordingly compvar = '_'.join([self.m_type, var]) #print compvar value = self.env[compvar] if value: self.env.appendValue(var, value) def apply_obj_vars(self): trace('apply_obj_vars called for cppobj') cpppath_st = self.env['CPPPATH_ST'] lib_st = self.env['LIB_ST'] staticlib_st = self.env['STATICLIB_ST'] libpath_st = self.env['LIBPATH_ST'] staticlibpath_st = self.env['STATICLIBPATH_ST'] # local flags come first # set the user-defined includes paths if not self._incpaths_lst: self.apply_incpaths() for i in self._bld_incpaths_lst: self.env.appendValue('_CXXINCFLAGS', cpppath_st % i.bldpath()) # set the library include paths for i in self.env['CPPPATH']: self.env.appendValue('_CXXINCFLAGS', cpppath_st % i) #print self.env['_CXXINCFLAGS'] #print " appending include ",i # this is usually a good idea self.env.appendValue('_CXXINCFLAGS', cpppath_st % '.') try: tmpnode = Params.g_build.m_curdirnode tmpnode_mirror = Params.g_build.m_tree.self.m_src_to_bld[tmpnode] self.env.appendValue('_CXXINCFLAGS', cpppath_st % tmpnode.bldpath()) self.env.appendValue('_CXXINCFLAGS', cpppath_st % tmpnode_mirror.bldpath()) except: pass for i in self.env['RPATH']: self.env.appendValue('LINKFLAGS', i) for i in self.env['LIBPATH']: self.env.appendValue('LINKFLAGS', libpath_st % i) for i in self.env['LIBPATH']: self.env.appendValue('LINKFLAGS', staticlibpath_st % i) if self.env['STATICLIB']: self.env.appendValue('LINKFLAGS', self.env['STATICLIB_MARKER']) for i in self.env['STATICLIB']: self.env.appendValue('LINKFLAGS', staticlib_st % i) if self.env['LIB']: self.env.appendValue('LINKFLAGS', self.env['SHLIB_MARKER']) for i in self.env['LIB']: self.env.appendValue('LINKFLAGS', lib_st % i) def install(self): if not (Params.g_commands['install'] or Params.g_commands['uninstall']): return if self.m_type == 'program': self.install_results('PREFIX', 'bin', self.m_linktask ) elif self.m_type == 'shlib': if self.want_libtool: self.install_results('PREFIX', 'lib', self.m_latask) if sys.platform=='win32' or not self.vnum: self.install_results('PREFIX', 'lib', self.m_linktask ) else: libname = self.m_linktask.m_outputs[0].m_name nums=self.vnum.split('.') name3 = libname+'.'+self.vnum name2 = libname+'.'+nums[0] name1 = libname filename = self.m_linktask.m_outputs[0].relpath_gen(Params.g_build.m_curdirnode) Common.install_as('PREFIX', 'lib/'+name3, filename) #print 'lib/'+name2, '->', name3 #print 'lib/'+name1, '->', name2 Common.symlink_as('PREFIX', name3, 'lib/'+name2) Common.symlink_as('PREFIX', name2, 'lib/'+name1) # static libraries are not to be installed so you will have to make a subclass #elif self.m_type == 'staticlib': # self.install_results('PREFIX', 'lib', self.m_linktask ) def apply_libtool(self): self.env['vnum']=self.vnum paths=[] libs=[] libtool_files=[] libtool_vars=[] for l in self.env['LINKFLAGS']: if l[:2]=='-L': paths.append(l[2:]) elif l[:2]=='-l': libs.append(l[2:]) for l in libs: for p in paths: try: dict = read_la_file(p+'/lib'+l+'.la') linkflags2 = dict['dependency_libs'] for v in linkflags2.split(): if v[len(v)-3:] == '.la': libtool_files.append(v) libtool_vars.append(v) continue self.env.appendUnique('LINKFLAGS', v) break except: pass self.env['libtoolvars']=libtool_vars while libtool_files: file = libtool_files.pop() dict = read_la_file(file) for v in dict['dependency_libs'].split(): if v[len(v)-3:] == '.la': libtool_files.append(v) continue self.env.appendUnique('LINKFLAGS', v) def apply_lib_vars(self): trace("apply_lib_vars called") # TODO complicated lookups, there are certainly ways to make it simple # TODO bad scheme, we are not certain that the node to depend on exists in the first place # well, at least we will throw an error message that makes sense libs = self.useliblocal.split() # store for use when calling "apply" sh_names = self.p_shlib_deps_names static_names = self.p_staticlib_deps_names tree = Params.g_build.m_tree for lib in libs: idx=len(lib)-1 while 1: idx = idx - 1 if lib[idx] == '/': break # find the path for linking and the library name path = lib[:idx] name = lib[idx+1:] lst = name.split('.') name = lst[0] ext = lst[1] trace('library found %s %s %s '%(str(name), str(path), str(ext))) if ext == 'a': type='staticlib' static_names.append(name) else: type='shlib' sh_names.append(name) # now that the name was added, find the corresponding node in the builddir dirnode = self.m_current_path.find_node( path.split('/') ) self.env.appendValue('LIBPATH', dirnode.srcpath()) # useful for the link path, but also for setting the dependency: try: dirnode = tree.get_mirror_node(dirnode) rname = self.get_library_name(name, type) node = dirnode.find_node([rname]) self.m_deps_linktask.append(node) except: print "dependency set on a node which does not exist!" print "", rname, " in ", dirnode print "" raise self.env.appendValue('LIB', sh_names) self.env.appendValue('STATICLIB', static_names) libs = self.uselib.split() for l in libs: for v in self.p_flag_vars: val='' try: val = self.env[v+'_'+l] except: pass if val: self.env.appendValue(v, val)